Я узнал больше о динамическом включении и расширении модулей с помощью метапрограммирования, и я нашел статью, обсуждающую реализацию #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