Ruby中的钩子方法详解

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

class User < Person
end

puts User.new.name

正如你所见,当 Person 类被其他子类继承时 inherited 类方法将会被调用。 运行以上代码结果如下:


User inherits Person
My name is Person

让我们看看 Rails 在它的代码中是如何使用 inherited 的。

Rails中的 inherited

Rails应用中有一个重要的类名为 Application ,定义中 config/application.rb 文件内。 这个类执行了许多不同的任务,如运行所有的Railties,引擎以及插件的初始化。 关于 Application 类的一个有趣的事件是,在同一个进程中不能运行两个实例。 如果我们尝试修改这个行为,Rails将会抛出一个异常。让我们来看看Rails是如何实现这个特性的。

Application 类继承自 Rails::Application,它是在这里定义的。 在62行定义了 inherited 钩子,它会在我们的Rails应用 Application 类继承 Rails::Application 时被调用。 inherited 钩子的代码如下:

class << self
  def inherited(base)
    raise "You cannot have more than one Rails::Application" if Rails.application
    super
    Rails.application = base.instance
    Rails.application.add_lib_to_load_path!
    ActiveSupport.run_load_hooks(:before_configuration, base.instance)
  end
end

class << self 是Ruby中的另一个定义类方法的方式。在 inherited 中的第1行是检查 Rails.application 是否已存在。 如果存在则抛出异常。第一次运行这段代码时 Rails.application 会返回false然后调用 super。 在这里 super 即是 Rails::Engine 的 inherited 钩子,因为 Rails::Application 继承自 Rails::Engine。

在下一行,你会看到 Rails.application 被赋值为 base.instance 。其余就是设置Rails应用了。

这就是Rails如何巧妙地使用 inherited 钩子来实现我们的Rails Application 类的单实例。

method_missing

method_missing 可能是Ruby中使用最广的钩子。在许多流行的Ruby框架/gem包/库中都有使用它。 当我们试图访问一个对象上不存在的方法时则会调用这个钩子方法。 让我们来看一个简单的例子:


class Person
  def name
    "My name is Person"
  end
end

p = Person.new

puts p.name     # => My name is Person
puts p.address  # => undefined method `address' for #<Person:0x007fb730a2b450> (NoMethodError)

我们定义了一个简单的 Person 类, 它只有一个 name 方法。然后创建一个 Person 的实例对象, 并分别调用 name 和 address 两个方法。因为 Person 中定义了 name,因此这个运行没问题。 然而 Person 并没有定义 address,这将会抛出一个异常。 method_missing 钩子可以优雅地捕捉到这些未定义的方法,避免此类异常。 让我们修改一下 Person 类: