在Devise中动态扩展模块

时间:2019-05-24 22:03:04

标签: ruby-on-rails ruby devise hook

我正在学习有关使用元编程动态包含和扩展模块的更多信息,并且发现了一篇文章,讨论了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

1 个答案:

答案 0 :(得分:1)

这两种方法都做同样的事情,因此由于历史原因(他们忘记删除)而导致冗余,或者它们并不依赖于所有具有self.included方法和{{1}方法的模块}