要把 model 中的代码拿出来, common pattern 是这样的:
module MyModule
def self.included(base)
base.extend ClassMethods
base.class_eval do
scope :disabled, -> { where(disabled: true) }
end
end
module ClassMethods
...
end
end
class MyClass
include MyModule
end
module MyModule
def self.included(base)
p 'hook'
end
end
class MyClass
include MyModule # 打印 'hook'
end
- included 是一个钩子方法, 当该 module 被 include 进某个类时, 会自动执行;
- included 接收参数(base), 值为 include 该 module 的类.
为什么要在 module 中用 def self.foo 的方式定义方法.
这样做之后, 这个方法可以被当做模块方法 调用, MyModule.foo, 不加 self 是不行的. module 是 class 的超类, self 的这种行为两者保持一致.
这个 module 被 mix-in 时, 该方法不会成为类的实例方法. 如果一个类 include 一个 module, 这个类会继承这个 module(实际上是这个 module 的代理类), 因此module 中定义的方法会成为类的实例方法. 而 module 中定义的 self.test 方法会成为什么样的方法呢? 会成为模块的方法, 但是不会成为代理类的方法, 所以这个类也无法获取的这个方法(这点存疑) 这里的关键问题在于, module 有单件类么, self.test 到底存在于哪里
在 module 里面写 extend self 会起到相同的作用.
http://stackoverflow.com/questions/11550213/ruby-module-function-vs-including-module
有个问题没搞清楚呢: 在 module 定义中里面 self 表示什么; 在其他地方, self 都表示当前对象; module 本身就是对象啊, 所以是给当前 module 增加了方法, 跟上文的解释是一致的. 如果是类的话, self 定义的方法, 其实是给类的单件类添加了实例方法; module 有没有单件类呢? 还是说只有代理类?
module MyModule
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
...
end
end
class MyClass
include MyModule
end
在钩子方法内部, 调用了base.extend ClassMethods
, ClassMethods 中定义的方法就成为了 Myclass 的类方法.这样等同于
module MyModule
...
module ClassMethods
...
end
end
class MyClass
include MyModule
extend MyModule::ClassMethods
end
但是很明显使用钩子方法好一些,这样在类中 include 该 module 时, 自动 extend 了定义类方法的 module.
module MyModule
def self.included(base)
base.class_eval do
scope :disabled, -> { where(disabled: true) }
end
end
end
class MyClass
include MyModule
end
这样实现的效果是
class MyClass
scope :disabled, -> { where(disabled: true) }
end
但是为什么呢?
第一步理解 class_eval class_eval 是在目标类的上下文中执行一个 block, 在 block 中定义的方法会成为目标类的实例方法, 很好理解.
MyClass.class_eval do
def hi
p 'hi'
end
end
MyClass.new.hi => 打印 hi
第二部理解 scope scope 是一种类宏(class macro).
类宏是可以在类定义中使用的普通方法, 看起来很像是关键字, 本质是在类的单例类中定义的实例方法.
所以说在调用类宏时, 应该在类的上下文中调用. problem solved, right?