module M
def self.included(base)
base.extend ClassMethods
base.class_eval do
scope :disabled, -> { where(disabled: true) }
end
include InstanceMethods
# include InstanceMethods shoule be base.include InstanceMethods
end
module ClassMethods
def say_hello
puts "say hello"
end
end
module InstanceMethods
def say_no
puts "say no"
end
end
end
我认为 each_setup 这个方法难理解的就在于它接收一个代码块,在代码块中又有代码块执行,分两步就可以搞清楚关系了。 举例说明:
lambda_1 = ->(setup) {setup.call} #这是一个可调用对象,它接收另外一个可调用对象作为参数
lambda_1.call("hehe") # 报错,因为参数不是可调用对象
lambda_1.call(Proc.new {puts "hehe"}) #=> hehe
回到书中的实例代码,在调用 each_setup 的时候挂载了一个代码块,
each_setup do |setup|
setup.call
end
#这里的setup.call不就相当于上面例子中的setup.call 嘛
#回到定义中,block.call(setup) 等价于
#proc_1 = ->(hehe) {hehe.call}
#proc_1.call(setup) 这里的setup会替换上一行中的hehe,也就是最后执行的是setup可调用对象
我的理解就是当一个 Speaker 的实例对象调用类 Speaker 中的 speak 方法时,根据 Ruby 的 Method lookup 机制, 先寻找到的是 action_module 中的 speak 方法。 因为我们之前在 action_module 中通过 define_method,动态的定义了一个 speak 方法,这个方法的实现就在代码块中。
send :define_method, options[:for] do |*args, &block|
send method_name
super(*args, &block)
end
所以先去调用 method_name 指代的方法,然后携带参数 (如果有的话),继续调用继承链上一级也就是类 Speaker 的 speak 方法。 这样就实现了 before_action 机制。
觉得 Ruby 真是自带鸡汤 before_action 这一段代码信息量不小。