在 method_added 中调用了未定义的自模块方法
Undefined self module method called in method_added
我正在尝试为 class 方法实现一个 hooker,比如 before_action
和 after_action
。问题是,如果我在模块内定义一个方法,define_method
或通常在 method_added
内使用 def do_before; puts 'do_before called'; end
则未定义。那么如何在 method_added
中调用 module method
?
module Hooker
[:before, :after].each do |element|
define_method("#{element}_action") do |name|
puts "#{element}_action called with parameter #{name}"
end
define_method("do_#{element}") do
puts "do_#{element} called"
end
end
def method_added(name)
return if @filtering # Don't add to original_ methods
@filtering = true
alias_method :"original_#{name}", name
define_method name do |*args|
do_before # undefined method `do_before' for #<Bar:0x007ff2f183c318>
self.send("original_#{name}", *args)
do_after # undefined method `do_after' for #<Bar:0x007ff2f183c318>
end
@filtering = false
end
end
class Bar
extend Hooker
before_action 'foo2'
after_action 'bar2'
def my_func
puts 'MyFunc called'
end
end
Bar.new.my_func
那是因为你正在使用
- extend
adds the specified module's methods and constants to the target's
metaclass
但你还需要 - include
it mixes in the specified module's methods as instance methods in the
target class
# some code goes here
class Bar
extend Hooker
include Hooker
before_action 'foo2'
after_action 'bar2'
def my_func
puts 'MyFunc called'
end
end
Bar.new.my_func
=> before_action called with parameter foo2
=> after_action called with parameter bar2
=> do_before called
=> MyFunc called
=> do_after called
更清楚的是将其分离到不同的模块。
这是一个替代解决方案,您无需在 class:
中显式调用 extend
和 include
module Hooker
def self.included(klass)
klass.extend ClassMethods
end
module ClassMethods
[:before, :after].each do |element|
define_method("#{element}_action") do |name|
puts "#{element}_action called with parameter #{name}"
end
define_method("do_#{element}") do
puts "do_#{element} called"
end
end
end
def method_added(name)
return if @filtering # Don't add to original_ methods
@filtering = true
alias_method :"original_#{name}", name
define_method name do |*args|
do_before
self.send("original_#{name}", *args)
do_after
end
@filtering = false
end
end
class Bar
include Hooker
before_action 'foo2'
after_action 'bar2'
def my_func
puts 'MyFunc called'
end
end
Bar.new.my_func
这里的技巧是 Hooker
模块现在 extend
ing though include
.
我正在尝试为 class 方法实现一个 hooker,比如 before_action
和 after_action
。问题是,如果我在模块内定义一个方法,define_method
或通常在 method_added
内使用 def do_before; puts 'do_before called'; end
则未定义。那么如何在 method_added
中调用 module method
?
module Hooker
[:before, :after].each do |element|
define_method("#{element}_action") do |name|
puts "#{element}_action called with parameter #{name}"
end
define_method("do_#{element}") do
puts "do_#{element} called"
end
end
def method_added(name)
return if @filtering # Don't add to original_ methods
@filtering = true
alias_method :"original_#{name}", name
define_method name do |*args|
do_before # undefined method `do_before' for #<Bar:0x007ff2f183c318>
self.send("original_#{name}", *args)
do_after # undefined method `do_after' for #<Bar:0x007ff2f183c318>
end
@filtering = false
end
end
class Bar
extend Hooker
before_action 'foo2'
after_action 'bar2'
def my_func
puts 'MyFunc called'
end
end
Bar.new.my_func
那是因为你正在使用
- extend
adds the specified module's methods and constants to the target's metaclass
但你还需要 - include
it mixes in the specified module's methods as instance methods in the target class
# some code goes here
class Bar
extend Hooker
include Hooker
before_action 'foo2'
after_action 'bar2'
def my_func
puts 'MyFunc called'
end
end
Bar.new.my_func
=> before_action called with parameter foo2
=> after_action called with parameter bar2
=> do_before called
=> MyFunc called
=> do_after called
更清楚的是将其分离到不同的模块。
这是一个替代解决方案,您无需在 class:
中显式调用extend
和 include
module Hooker
def self.included(klass)
klass.extend ClassMethods
end
module ClassMethods
[:before, :after].each do |element|
define_method("#{element}_action") do |name|
puts "#{element}_action called with parameter #{name}"
end
define_method("do_#{element}") do
puts "do_#{element} called"
end
end
end
def method_added(name)
return if @filtering # Don't add to original_ methods
@filtering = true
alias_method :"original_#{name}", name
define_method name do |*args|
do_before
self.send("original_#{name}", *args)
do_after
end
@filtering = false
end
end
class Bar
include Hooker
before_action 'foo2'
after_action 'bar2'
def my_func
puts 'MyFunc called'
end
end
Bar.new.my_func
这里的技巧是 Hooker
模块现在 extend
ing though include
.