自定义 Hook/Callback/Macro 方法
Custom Hook/Callback/Macro Methods
如何在子类中创建自定义挂钩方法?
无需重复Rails,当然——越简单越好。
我的目标是转换:
class SubClass
def do_this_method
first_validate_something
end
def do_that_method
first_validate_something
end
private
def first_validate_something; end
end
收件人:
class ActiveClass; end
class SubClass < ActiveClass
before_operations :first_validate_something, :do_this_method, :do_that_method
def do_this_method; end
def do_that_method; end
private
def first_validate_something; end
end
模块中的示例:https://github.com/PragTob/after_do/blob/master/lib/after_do.rb
Rails#before_action:http://apidock.com/rails/v4.0.2/AbstractController/Callbacks/ClassMethods/before_action
您可以将原始方法别名为不同的名称(因此 :do_this_something
变为 :original_do_this_something
),然后定义一个新的 :do_this_something
方法调用 :first_validate_something
然后该方法的原始版本 像这样...
class ActiveClass
def self.before_operations(before_method, *methods)
methods.each do |method|
alias_method "original_#{method.to_s}".to_sym, method
define_method(method, *args, &block) do
send before_method
send "original_#{method.to_s}", *args, &block
end
end
end
end
这是一个使用 prepend
的解决方案。当您第一次调用 before_operations
时,它会创建一个新的(空)模块并将其添加到您的 class 之前。这意味着当您在 class 上调用方法 foo
时,它将首先在模块中查找该方法。
before_operations
方法然后在此模块中定义简单方法,首先调用您的 'before' 方法,然后使用 super
调用您的 class 中的实际实现.
class ActiveClass
def self.before_operations(before_method,*methods)
prepend( @active_wrapper=Module.new ) unless @active_wrapper
methods.each do |method_name|
@active_wrapper.send(:define_method,method_name) do |*args,&block|
send before_method
super(*args,&block)
end
end
end
end
class SubClass < ActiveClass
before_operations :first_validate_something, :do_this_method, :do_that_method
def do_this_method(*args,&block)
p doing:'this', with:args, and:block
end
def do_that_method; end
private
def first_validate_something
p :validating
end
end
SubClass.new.do_this_method(3,4){ |x| p x }
#=> :validating
#=> {:doing=>"this", :with=>[3, 4], :and=>#<Proc:0x007fdb1301fa18@/tmp.rb:31>}
如果你想让@SteveTurczyn 的想法付诸实践,你必须:
- 接收
define_method
块中的 args 参数,而不是它的参数。
- 如果您希望能够为它们设置别名,请在定义方法后调用
before_operations
。
class ActiveClass
def self.before_operations(before_method, *methods)
methods.each do |meth|
raise "No method `#{meth}` defined in #{self}" unless method_defined?(meth)
orig_method = "_original_#{meth}"
alias_method orig_method, meth
define_method(meth) do |*args,&block|
send before_method
send orig_method, *args, &block
end
end
end
end
class SubClass < ActiveClass
def do_this_method(*args,&block)
p doing:'this', with:args, and:block
end
def do_that_method; end
before_operations :first_validate_something, :do_this_method, :do_that_method
private
def first_validate_something
p :validating
end
end
SubClass.new.do_this_method(3,4){ |x| p x }
#=> :validating
#=> {:doing=>"this", :with=>[3, 4], :and=>#<Proc:0x007fdb1301fa18@/tmp.rb:31>}
这是一种不使用别名的代码编写方式。它包括一个 class 方法 validate
指定验证器方法和调用验证器方法的方法。此方法 validate
可以多次调用以动态更改验证器和被验证器。
class ActiveClass
end
将验证器以外的所有方法放在 ActiveClass
的子 class 中,命名为(比如)MidClass
.
class MidClass < ActiveClass
def do_this_method(v,a,b)
puts "this: v=#{v}, a=#{a}, b=#{b}"
end
def do_that_method(v,a,b)
puts "that: v=#{v}, a=#{a}, b=#{b}"
end
def yet_another_method(v,a,b)
puts "yet_another: v=#{v}, a=#{a}, b=#{b}"
end
end
MidClass.instance_methods(false)
#=> [:do_this_method, :do_that_method, :yet_another_method]
将验证器与 class 方法 validate
一起放在 MidClass
的子 class 中,命名为(比如)SubClass
。
class SubClass < MidClass
def self.validate(validator, *validatees)
superclass.instance_methods(false).each do |m|
if validatees.include?(m)
define_method(m) do |v, *args|
send(validator, v)
super(v, *args)
end
else
define_method(m) do |v, *args|
super(v, *args)
end
end
end
end
private
def validator1(v)
puts "valid1, v=#{v}"
end
def validator2(v)
puts "valid2, v=#{v}"
end
end
SubClass.methods(false)
#=> [:validate]
SubClass.private_instance_methods(false)
#=> [:validator1, :validator2]
class 方法validate
为要使用的验证方法和要验证的方法传递符号。我们来试试吧。
sc = SubClass.new
SubClass.validate(:validator1, :do_this_method, :do_that_method)
sc.do_this_method(1,2,3)
# valid1, v=1
# this: v=1, a=2, b=3
sc.do_that_method(1,2,3)
# valid1, v=1
# that: v=1, a=2, b=3
sc.yet_another_method(1,2,3)
# yet_another: v=1, a=2, b=3
现在更改验证。
SubClass.validate(:validator2, :do_that_method, :yet_another_method)
sc.do_this_method(1,2,3)
# this: v=1, a=2, b=3
sc.do_that_method(1,2,3)
# valid2, v=1
# that: v=1, a=2, b=3
sc.yet_another_method(1,2,3)
# valid2, v=1
# yet_another: v=1, a=2, b=3
当调用 super
时没有来自普通方法的参数,所有参数和一个块(如果有的话)都会传递给 super。但是,如果该方法是使用 define_method
创建的,则没有参数(也没有块)传递给 super。在后一种情况下,参数必须是明确的。
我想将一个方块或过程传递给 super
(如果有的话),但一直使用错误的秘方。我会欢迎这样做的建议。
如何在子类中创建自定义挂钩方法?
无需重复Rails,当然——越简单越好。
我的目标是转换:
class SubClass
def do_this_method
first_validate_something
end
def do_that_method
first_validate_something
end
private
def first_validate_something; end
end
收件人:
class ActiveClass; end
class SubClass < ActiveClass
before_operations :first_validate_something, :do_this_method, :do_that_method
def do_this_method; end
def do_that_method; end
private
def first_validate_something; end
end
模块中的示例:https://github.com/PragTob/after_do/blob/master/lib/after_do.rb
Rails#before_action:http://apidock.com/rails/v4.0.2/AbstractController/Callbacks/ClassMethods/before_action
您可以将原始方法别名为不同的名称(因此 :do_this_something
变为 :original_do_this_something
),然后定义一个新的 :do_this_something
方法调用 :first_validate_something
然后该方法的原始版本 像这样...
class ActiveClass
def self.before_operations(before_method, *methods)
methods.each do |method|
alias_method "original_#{method.to_s}".to_sym, method
define_method(method, *args, &block) do
send before_method
send "original_#{method.to_s}", *args, &block
end
end
end
end
这是一个使用 prepend
的解决方案。当您第一次调用 before_operations
时,它会创建一个新的(空)模块并将其添加到您的 class 之前。这意味着当您在 class 上调用方法 foo
时,它将首先在模块中查找该方法。
before_operations
方法然后在此模块中定义简单方法,首先调用您的 'before' 方法,然后使用 super
调用您的 class 中的实际实现.
class ActiveClass
def self.before_operations(before_method,*methods)
prepend( @active_wrapper=Module.new ) unless @active_wrapper
methods.each do |method_name|
@active_wrapper.send(:define_method,method_name) do |*args,&block|
send before_method
super(*args,&block)
end
end
end
end
class SubClass < ActiveClass
before_operations :first_validate_something, :do_this_method, :do_that_method
def do_this_method(*args,&block)
p doing:'this', with:args, and:block
end
def do_that_method; end
private
def first_validate_something
p :validating
end
end
SubClass.new.do_this_method(3,4){ |x| p x }
#=> :validating
#=> {:doing=>"this", :with=>[3, 4], :and=>#<Proc:0x007fdb1301fa18@/tmp.rb:31>}
如果你想让@SteveTurczyn 的想法付诸实践,你必须:
- 接收
define_method
块中的 args 参数,而不是它的参数。 - 如果您希望能够为它们设置别名,请在定义方法后调用
before_operations
。
class ActiveClass
def self.before_operations(before_method, *methods)
methods.each do |meth|
raise "No method `#{meth}` defined in #{self}" unless method_defined?(meth)
orig_method = "_original_#{meth}"
alias_method orig_method, meth
define_method(meth) do |*args,&block|
send before_method
send orig_method, *args, &block
end
end
end
end
class SubClass < ActiveClass
def do_this_method(*args,&block)
p doing:'this', with:args, and:block
end
def do_that_method; end
before_operations :first_validate_something, :do_this_method, :do_that_method
private
def first_validate_something
p :validating
end
end
SubClass.new.do_this_method(3,4){ |x| p x }
#=> :validating
#=> {:doing=>"this", :with=>[3, 4], :and=>#<Proc:0x007fdb1301fa18@/tmp.rb:31>}
这是一种不使用别名的代码编写方式。它包括一个 class 方法 validate
指定验证器方法和调用验证器方法的方法。此方法 validate
可以多次调用以动态更改验证器和被验证器。
class ActiveClass
end
将验证器以外的所有方法放在 ActiveClass
的子 class 中,命名为(比如)MidClass
.
class MidClass < ActiveClass
def do_this_method(v,a,b)
puts "this: v=#{v}, a=#{a}, b=#{b}"
end
def do_that_method(v,a,b)
puts "that: v=#{v}, a=#{a}, b=#{b}"
end
def yet_another_method(v,a,b)
puts "yet_another: v=#{v}, a=#{a}, b=#{b}"
end
end
MidClass.instance_methods(false)
#=> [:do_this_method, :do_that_method, :yet_another_method]
将验证器与 class 方法 validate
一起放在 MidClass
的子 class 中,命名为(比如)SubClass
。
class SubClass < MidClass
def self.validate(validator, *validatees)
superclass.instance_methods(false).each do |m|
if validatees.include?(m)
define_method(m) do |v, *args|
send(validator, v)
super(v, *args)
end
else
define_method(m) do |v, *args|
super(v, *args)
end
end
end
end
private
def validator1(v)
puts "valid1, v=#{v}"
end
def validator2(v)
puts "valid2, v=#{v}"
end
end
SubClass.methods(false)
#=> [:validate]
SubClass.private_instance_methods(false)
#=> [:validator1, :validator2]
class 方法validate
为要使用的验证方法和要验证的方法传递符号。我们来试试吧。
sc = SubClass.new
SubClass.validate(:validator1, :do_this_method, :do_that_method)
sc.do_this_method(1,2,3)
# valid1, v=1
# this: v=1, a=2, b=3
sc.do_that_method(1,2,3)
# valid1, v=1
# that: v=1, a=2, b=3
sc.yet_another_method(1,2,3)
# yet_another: v=1, a=2, b=3
现在更改验证。
SubClass.validate(:validator2, :do_that_method, :yet_another_method)
sc.do_this_method(1,2,3)
# this: v=1, a=2, b=3
sc.do_that_method(1,2,3)
# valid2, v=1
# that: v=1, a=2, b=3
sc.yet_another_method(1,2,3)
# valid2, v=1
# yet_another: v=1, a=2, b=3
当调用 super
时没有来自普通方法的参数,所有参数和一个块(如果有的话)都会传递给 super。但是,如果该方法是使用 define_method
创建的,则没有参数(也没有块)传递给 super。在后一种情况下,参数必须是明确的。
我想将一个方块或过程传递给 super
(如果有的话),但一直使用错误的秘方。我会欢迎这样做的建议。