从函数覆盖访问外部属性
Access outer attribute from a function override
我有一个包装器 class,它重新定义了包装器 class 的方法。有什么方法可以从覆盖方法内部访问包装器的状态?
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
用一个非常愚蠢的 hack 解决了这个问题 - 使用 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
其实也没那么难。几点:
- 您可能想调用原始
save_result
。否则,它不是一个包装器。
您需要使用闭包来捕获当前的词汇上下文(意思是,记住我们在 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
我不太明白你的问题,但我认为过去做过类似的事情,也许以下几行可以帮助你:
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
根据你的例子,我会 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
我有一个包装器 class,它重新定义了包装器 class 的方法。有什么方法可以从覆盖方法内部访问包装器的状态?
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
用一个非常愚蠢的 hack 解决了这个问题 - 使用 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
其实也没那么难。几点:
- 您可能想调用原始
save_result
。否则,它不是一个包装器。 您需要使用闭包来捕获当前的词汇上下文(意思是,记住我们在
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
我不太明白你的问题,但我认为过去做过类似的事情,也许以下几行可以帮助你:
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
根据你的例子,我会 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