从函数覆盖中访问外部属性

时间:2015-08-04 10:49:45

标签: ruby metaprogramming

我有一个包装类,它重新定义了包装类的方法。有没有办法可以从覆盖方法中访问包装器的状态?

class WidgetWrapper

  attr_accessor :result_saved_by_widget

  def initialize(widget)
    @widget = widget
    # we intercept the widget's usual "save" method so we can see
    # what the widget tries to save
    def @widget.save_result(result) # this override works fine ...
      OUTER.result_saved_by_widget = result # .. but I need something like this inside it!
    end
  end

  def call
    widget.calculate # this will call "save_result" at some stage
  end

end

# How it gets used
wrapper = Wrapper.new(Widget.new)
wrapper.call
puts wrapper.result_saved_by_widget

4 个答案:

答案 0 :(得分:2)

根据你的例子,我会extend带有模块的对象:

module WidgetExtension
  attr_accessor :results_saved_by_widget

  def save_result(result)
    @results_saved_by_widget = result
    super
  end
end


w = Widget.new
w.extend(WidgetExtension)
w.calculate

w.results_saved_by_widget #=> stored value

答案 1 :(得分:1)

使用一个非常愚蠢的黑客来解决这个问题 - 事先使用instance_variable_set注入包装器对象。

class WidgetWrapper

  attr_accessor :result_saved_by_widget

  def initialize(widget)
    @widget = widget
    @widget.instance_variable_set :@wrapper, self
    # we intercept the widget's usual "save" method so we can see
    # what the widget tries to save
    def @widget.save_result(result) # this override works fine ...
      @wrapper.result_saved_by_widget = result # ... and this works too :)
    end
  end

  def call
    widget.calculate # this will call "save_result" at some stage
  end

end

# How it gets used
wrapper = Wrapper.new(Widget.new)
wrapper.call
puts wrapper.result_saved_by_widget

答案 2 :(得分:1)

实际上,并不难。几点:

  1. 您可能想要拨打原始save_result。否则,它不是一个包装器。
  2. 你需要使用闭包来捕捉当前的词汇上下文(意思是,记住我们在WidgetWrapper中的内容)

    class Widget
      def calculate
        save_result(3)
      end
    
      def save_result(arg)
        puts "original save_result: #{arg}"
      end
    end
    
    class WidgetWrapper
    
      attr_accessor :result_saved_by_widget, :widget
    
      def initialize(widget)
        @widget = widget
    
        wrapper = self # `self` can/will unpredictably change.
    
    
        @widget.define_singleton_method :save_result do |result|
          wrapper.result_saved_by_widget = result
          super(result)
        end
      end
    
      def call
        widget.calculate
      end
    
    end
    
    # How it gets used
    wrapper = WidgetWrapper.new(Widget.new)
    wrapper.call
    puts 'intercepted value'
    puts wrapper.result_saved_by_widget
    # >> original save_result: 3
    # >> intercepted value
    # >> 3
    

答案 3 :(得分:1)

我不太了解你的问题,但我认为过去做的事情非常相似,也许以下几行可以帮到你:

documents_to_wrap.each do |doc|
  doc.define_singleton_method(:method){override_code}
  tmp = doc.instance_variable_get(:@instance_var).
  doc.instance_variable_set(:@other_instance_var, tmp.do_something)
end