我正在学习有关使用元编程动态包含和扩展模块的更多信息,并且发现了一篇文章,讨论了Devise gem中#included
的实现。要包括devise模块,请将以下行添加到模型中:
devise :database_authenticatable, :registerable, :validatable
这会触发devise
方法:
def devise(*modules)
options = modules.extract_options!.dup
selected_modules = modules.map(&:to_sym).uniq.sort_by do |s|
Devise::ALL.index(s) || -1 # follow Devise::ALL order
end
devise_modules_hook! do
include Devise::Models::Authenticatable
selected_modules.each do |m|
mod = Devise::Models.const_get(m.to_s.classify)
if mod.const_defined?("ClassMethods")
class_mod = mod.const_get("ClassMethods")
extend class_mod
if class_mod.respond_to?(:available_configs)
available_configs = class_mod.available_configs
available_configs.each do |config|
next unless options.key?(config)
send(:"#{config}=", options.delete(config))
end
end
end
include mod
end
self.devise_modules |= selected_modules
options.each { |key, value| send(:"#{key}=", value) }
end
end
然后是included
模块中的Validatable
方法的示例:
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
我的问题是,如果ClassMethods
模块已经在devise
方法中扩展了(下面的特定代码块),为什么又在#included
中扩展了它的基数呢?仅仅是冗余,还是它们有不同的行为?
# This is from the `devise` method
if mod.const_defined?("ClassMethods")
class_mod = mod.const_get("ClassMethods")
extend class_mod
# This is the first line of the `self.included(base)` method
base.extend ClassMethods
答案 0 :(得分:1)
这两种方法都做同样的事情,因此由于历史原因(他们忘记删除)而导致冗余,或者它们并不依赖于所有具有self.included
方法和{{1}方法的模块}