萬盛學電腦網

 萬盛學電腦網 >> 網絡編程 >> 編程語言綜合 >> Ruby中的鉤子方法詳解

Ruby中的鉤子方法詳解

   這篇文章主要介紹了Ruby中的鉤子方法詳解,本文講解了什麼是鉤子方法、included、Devise中的 included、extended、ActiveRecord中的 extended、prepended、inherited等內容,需要的朋友可以參考下

  Ruby的哲學理念是基於一個基本的要素,那就是讓程序員快樂。Ruby非常注重程序員的快樂,並且也提供了許多不同的方法來實現它。 它的元編程能力能夠讓程序員編寫在運行時動態生成的代碼。它的線程功能使得程序員有一種優雅的的方式編寫多線程代碼。 它的鉤子方法能讓程序員在程序運行時擴展它的行為。

  上述的這些特性,以及一些其他很酷的語言方面,使得Ruby成為編寫代碼的優先選擇之一。 本文將探討Ruby中的一些重要的鉤子方法。我們將從不同方面討論鉤子方法,如它們是什麼,它們用於什麼,以及我們如何使用它們來解決不同的問題。 我們同時也了解一下一些流行的Ruby框架/Gem包/庫是如何使用它們來提供非常酷的特性的。

  我們開始吧。

  什麼是鉤子方法?

  鉤子方法提供了一種方式用於在程序運行時擴展程序的行為。 假設有這樣的功能,可以在無論何時一個子類繼承了一些特定的父類時收到通知, 或者是比較優雅地處理一個對象上的不可調用的方法而不是讓編譯器拋出異常。 這些情況就是使用鉤子方法,但是它們的用法並不僅限於此。 不同的框架/庫使用了不同的鉤子方法來實現它們的功能。

  在本文中我們將會討論如下幾個鉤子方法:

  1.included

  2.extended

  3.prepended

  4.inherited

  5.method_missing

  included

  Ruby給我們提供了一種方式使用 模塊(modules) (在其他語言中被稱作 混入類(mixins))來編寫模塊化的代碼供其他的 模塊/類 使用。 模塊 的概念很簡單,它就是一個可以在其他地方使用的獨立代碼塊。

  例如,如果我們想要編寫一些代碼在任何時候調用特定的方法都會返回一個靜態字符串。 我們姑且將這個方法稱作 name。你可能在其他地方也會想使用同一塊代碼。 這樣最好是新建一個模塊。讓我們來創建一個:

   代碼如下:

  module Person

  def name

  puts "My name is Person"

  end

  end

  這是一個非常簡單的模塊,僅有一個 name 方法用於返回一個靜態字符串。在我們的程序中使用這個模塊:

  代碼如下:

  class User

  include Person

  end

  Ruby提供了一些不同的方法來使用模塊。include 是其中之一。include 所做的就是將在 module 內定義的方法在一個 class 的實例變量上可用。 在我們的例子中,是將 Person 模塊中定義的方法變為一個 User 類實例對象的方法。 這就相當於我們是將 name 方法寫在 User 類裡一樣,但是定義在 module 裡的好處是可復用。 要調用 name 方法我們需要創建一個 User 的實例對象,然後再在這個對象上調用 name 方法。例如:

   代碼如下:

  User.new.name

  => My name is Person

  讓我們看看基於 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

  end

  end

  include mod

  end

  self.devise_modules |= selected_modules

  options.each { |key, value| send(:"#{key}=", value) }

  end

  end

  在我們的模型中傳給 devise 方法的模塊名將會作為一個數組保存在 *modules 中。 對於傳入的模塊調用 extract_options! 方法提取可能傳入的選項。 在11行中調用 each 方法,並且每個模塊在代碼塊中用 m 表示。 在12行中 m 將會轉化為一個常量(類名),因此使用 m.to.classify 一個例如 :validatable 這樣的符號會變為 Validatable 。 隨便說一下 classify 是ActiveSupport的方法。

  Devise::Models.const_get(m.to_classify) 會獲取該模塊的引用,並賦值給 mod。 在27行使用 include mod 包含該模塊。 例子中的 Validatable 模塊是定義在這裡。 Validatable 的 included 鉤子方法定義如下:

   代碼如下:

  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

  此時模型是 base。在第5行的 class_eval 代碼塊會以該類作為上下文進行求值運算。 通過 class_eval 編寫的代碼與直接打開該類的文件將代碼粘貼進去效果是一樣的。 Devise是通過 class_eval 將驗證包含到我們的用戶模型中的。

  當我們試著使用Devise注冊或者登錄時,我們會看到這些驗證,但是我們並沒有編寫這些驗證代碼。 Devise是利用了 included 鉤子來實現這些的。非常的優雅吧。

  extended

  Ruby也允許開發者 擴展(extend) 一個模塊,這與 包含(include) 有點不同。 extend 是將定義在 模塊(module) 內的方法應用為類的方法,而不是實例的方法。 讓我們來看一個簡單的例子:

  代碼如下:

  module Person

  def name

  "My name is Person"

  end

  end

  class User

  extend Person

  end

  puts User.name # => My name is Person

  正如你所看到的,我們將 Person 模塊內定義的 name 方法作為了 User 的類方法調用。 extend 將 Person 模塊內的方法添加到了 User 類中。extend 同樣也可以用於將模塊內的方法作為單例方法(singleton methods)。 讓我們再來看另外一個例子:

   代碼如下:

  # We are using same Person module and User class from previous example.

  u1 = User.new

  u2 = User.new

  u1.extend Person

  puts u1.name # => My name is Person

  puts u2.name # => undefined method `name' for # (NoMethodError)

  我們創建了兩個 User 的實例對象,並將 Person 作為參數在 u1 上調用 extend 方法。 使用這種調用方式,Person 的 name 方法僅對 u1 有效,對於其他

copyright © 萬盛學電腦網 all rights reserved