end
end
include mod
end
self.devise_modules |= selected_modules
options.each { |key, value| send(:"#{key}=", value) }
end
end
在我们的模型中传给 devise 方法的模块名将会作为一个数组保存在 *modules 中。 对于传入的模块调用 extract_options! 方法提取可能传入的选项。 在11行中调用 each 方法,并且每个模块在代码块中用 m 表示。 在12行中 m 将会转化为一个常量(类名),因此使用 m.to.classify 一个例如 :validatable 这样的符号会变为 Validatable 。 随便说一下 classify 是ActiveSupport的方法。
Devise::Models.const_get(m.to_classify) 会获取该模块的引用,并赋值给 mod。 在27行使用 include mod 包含该模块。 例子中的 Validatable 模块是定义在这里。 Validatable 的 included 钩子方法定义如下:
def self.included(base)
base.extend ClassMethods
assert_validations_api!(base)
base.class_eval do
validates_presence_of :email, if: :email_required?
validates_uniqueness_of :email, allow_blank: true, if: :email_changed?
validates_format_of :email, with: email_regexp, allow_blank: true, if: :email_changed?
validates_presence_of :password, if: :password_required?
validates_confirmation_of :password, if: :password_required?
validates_length_of :password, within: password_length, allow_blank: true
end
end
此时模型是 base。在第5行的 class_eval 代码块会以该类作为上下文进行求值运算。 通过 class_eval 编写的代码与直接打开该类的文件将代码粘贴进去效果是一样的。 Devise是通过 class_eval 将验证包含到我们的用户模型中的。
当我们试着使用Devise注册或者登录时,我们会看到这些验证,但是我们并没有编写这些验证代码。 Devise是利用了 included 钩子来实现这些的。非常的优雅吧。
extended
Ruby也允许开发者 扩展(extend) 一个模块,这与 包含(include) 有点不同。 extend 是将定义在 模块(module) 内的方法应用为类的方法,而不是实例的方法。 让我们来看一个简单的例子:
module Person
def name
"My name is Person"
end
end
class User
extend Person
end
puts User.name # => My name is Person
正如你所看到的,我们将 Person 模块内定义的 name 方法作为了 User 的类方法调用。 extend 将 Person 模块内的方法添加到了 User 类中。extend 同样也可以用于将模块内的方法作为单例方法(singleton methods)。 让我们再来看另外一个例子:










