Ruby中的钩子方法详解

2019-09-25 09:41:20于丽


让我们看看基于 include 的钩子方法。included 是Ruby提供的一个钩子方法,当你在一些 module 或者 class 中 include 了一个 module 时它会被调用。 更新 Person 模块:


module Person
  def self.included(base)
    puts "#{base} included #{self}"
  end

  def name
    "My name is Person"
  end
end

你可以看到一个新的方法 included 被定义为 Person 模块的类方法。当你在其他的模块或者类中执行 include Person 时,这个 included 方法会被调用。 该方法接收的一个参数是对包含该模块的类的引用。试试运行 User.new.name,你会看到如下的输出:


User included Person
My name is Person

正如你所见,base 返回的是包含该模块的类名。现在我们有了一个包含 Person 模块的类的引用,我们可以通过元编程来实现我们想要的功能。 让我们来看看 Devise是如何使用 included 钩子的。

Devise中的 included

Devise是Ruby中使用最广泛的身份验证gem包之一。它主要是由我喜欢的程序员 José Valim 开发的,现在是由一些了不起的贡献者在维护。 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