Skip to content

Latest commit

 

History

History
57 lines (45 loc) · 1.75 KB

File metadata and controls

57 lines (45 loc) · 1.75 KB

Back to main README

Ruby metaprogramming

Specs: spec/attr_accessor_with_history_spec.rb

Skeleton: lib/attr_accessor_with_history.rb

In lecture we saw how attr_accessor uses metaprogramming to create getters and setters for object attributes on the fly.

Define a method attr_accessor_with_history that provides the same functionality as attr accessor but also tracks every value the attribute has ever had:

    class Foo 
      attr_accessor_with_history :bar
    end
    f = Foo.new        # => #<Foo:0x127e678>
    f.bar = 3          # => 3
    f.bar = :wowzo     # => :wowzo
    f.bar = 'boo!'     # => 'boo!'
    f.bar_history      # => [nil, 3, :wowzo]

(Calling bar_history before bar's setter is ever called should return nil.)

History of instance variables should be maintained separately for each object instance. that is:

    f = Foo.new
    f.bar = 1 ; f.bar = 2
    g = Foo.new
    g.bar = 3 ; g.bar = 4
    g.bar_history

then the last line should just return [nil,3], rather than [nil,1,2,3].

If you're interested in how the template works, the first thing to notice is that if we define attr_accessor_with_history in class Class, we can use it as in the snippet above. This is because a Ruby class like Foo or String is actually just an object of class Class. (If that makes your brain hurt, just don't worry about it for now. It'll come.)

The second thing to notice is that Ruby provides a method class_eval that takes a string and evaluates it in the context of the current class, that is, the class from which you're calling attr_accessor_with_history. This string will need to contain a method definition that implements a setter-with-history for the desired attribute attr_name.