Ruby - Using class_eval to define methods

后端 未结 3 1994
情深已故
情深已故 2020-12-23 10:45

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:<

相关标签:
3条回答
  • 2020-12-23 11:02

    You must notice that #{attr_name}_history is a instance variable, so use @ before, like @foo in the class below

    def #{attr_name}=value, #{attr_name}= is the method name, value is parameter, same as def func parameter

    def #{attr_name}=value
      (!defined? @#{attr_name}_history) ? @#{attr_name}_history = [nil, value] : @#{attr_name}_history << value
    end
    
    0 讨论(0)
  • 2020-12-23 11:10

    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

    0 讨论(0)
  • 2020-12-23 11:18

    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.

    0 讨论(0)
提交回复
热议问题