Ruby.Metaprogramming. class_eval

前端 未结 5 681
孤独总比滥情好
孤独总比滥情好 2020-12-18 16:50

There seem to be a mistake in my code. However I just can\'t find it out.

class Class
def attr_accessor_with_history(attr_name)
  attr_name = attr_name.to_s
         


        
5条回答
  •  遥遥无期
    2020-12-18 17:30

    @Sergio Tulentsev's answer works, but it promotes a problematic practice of using string eval which is in general fraught with security risks and other surprises when the inputs aren't what you expect. For example, what happens to Sergio's version if one calls (no don't try it):

    attr_accessor_with_history %q{foo; end; system "rm -rf /"; def foo}
    

    It is often possible to do ruby meta-programming more carefully without string eval. In this case, using simple interpolation and define_method of closures with instance_variable_[get|set], and send:

    module History
    
      def attr_accessor_with_history(attr_name)
        getter_sym  = :"#{attr_name}"
        setter_sym  = :"#{attr_name}="
        history_sym = :"#{attr_name}_history"
        iv_sym      = :"@#{attr_name}"
        iv_hist     = :"@#{attr_name}_history"
    
        define_method getter_sym do
          instance_variable_get(iv_sym)
        end
    
        define_method setter_sym do |val|
          instance_variable_set( iv_hist, [] ) unless send(history_sym)
          send(history_sym).send( :'<<', send(getter_sym) )
          instance_variable_set( iv_sym, val @)
        end
    
        define_method history_sym do
          instance_variable_get(iv_hist)
        end
    
      end
    end
    

提交回复
热议问题