如何在子类中创建自定义挂钩方法?
当然不需要复制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
答案 0 :(得分:3)
您可以将原始方法替换为其他名称(因此: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
答案 1 :(得分:3)
以下是使用prepend
的解决方案。当您第一次致电before_operations
时,它会创建一个新的(空)模块并将其预先添加到您的班级。这意味着当您在类上调用方法foo
时,它将首先查看模块中的该方法。
before_operations
方法然后在此模块中定义简单方法,首先调用'before'方法,然后使用super
调用类中的实际实现。
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 params,而不是作为参数。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>}
答案 2 :(得分:1)
这是一种编写不使用别名的代码的方法。它包括一个类方法validate
,它指定验证器方法和调用验证器方法的方法。可以多次调用此方法validate
以更改验证程序并动态验证。
class ActiveClass
end
将除验证者以外的所有方法放在ActiveClass
的子类中(名为)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]
将验证器与类方法validate
一起放在名为MidClass
的{{1}}的子类中。
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]
传递要使用的验证方法的符号和要验证的方法。我们来试试吧。
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
创建方法,则不会将任何参数(并且没有块)传递给super。在后一种情况下,参数必须是明确的。
我想将一个块或proc传递给define_method
,如果有的话,但是一直使用错误的秘密酱。我会欢迎这样做的建议。