Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby - Using class_eval to define methods

Tags:

I'm doing the SaaS Stanford class, trying to do Part 5 of this assignment

I'm having a really hard time grasping this concept, this is what I've attempted to do:

class Class   def attr_accessor_with_history(attr_name)     attr_name = attr_name.to_s     attr_reader attr_name     attr_reader attr_name + '_history'     class_eval %Q'{def #{attr_name}(a);#{attr_name}_history.push(a) ; end;}'   end end 

I'm probably doing all sorts of things wrong, read The Book Of Ruby chapter on metaprogramming and I still don't get it, can someone help me comprehend this?

like image 268
8vius Avatar asked Mar 05 '12 02:03

8vius


2 Answers

This was fun!!!

class Class     def attr_accessor_with_history(attr_name)         attr_name = attr_name.to_s # make sure it's a string         attr_reader attr_name         attr_reader attr_name+"_history"         class_eval %Q"             def #{attr_name}=(value)                 if !defined? @#{attr_name}_history                     @#{attr_name}_history = [@#{attr_name}]                 end                 @#{attr_name} = value                 @#{attr_name}_history << value             end         "     end end  class Foo     attr_accessor_with_history :bar end  class Foo2     attr_accessor_with_history :bar     def initialize()         @bar = 'init'     end end  f = Foo.new f.bar = 1 f.bar = nil f.bar = '2' f.bar = [1,nil,'2',:three] f.bar = :three puts "First bar:", f.bar.inspect, f.bar_history.inspect puts "Correct?", f.bar_history == [f.class.new.bar, 1, nil, '2', [1,nil,'2',:three], :three] ? "yes" : "no" old_bar_history = f.bar_history.inspect  f2 = Foo2.new f2.bar = 'baz' f2.bar = f2 puts "\nSecond bar:", f2.bar.inspect, f2.bar_history.inspect puts "Correct?", f2.bar_history == [f2.class.new.bar, 'baz', f2] ? "yes" : "no"  puts "\nIs the old f.bar intact?", f.bar_history.inspect == old_bar_history ? "yes" : "no" 

Note that the only reason you need to use strings with class_eval is so that you can refer to the value of attr_name when defining the custom setter. Otherwise one would normally pass a block to class_eval.

like image 51
Irfy Avatar answered Oct 20 '22 17:10

Irfy


With regard to what you've done you're actually on the cusp of the solution. It's just that #{attr_name}_history doesn't exist in your code. You will need to create an instance variable and set it to nil if it doesn't exist. What you have already should handle pushing into the array if it does exist.

There are several ways to do that. One way is if defined? @#{attr_name}_history DoStuffHere

like image 36
Angel Marquez Avatar answered Oct 20 '22 18:10

Angel Marquez