v7.1.3.2
更多資訊請至 rubyonrails.org: 更多 Ruby on Rails

建立 Rails 外掛程式基礎

Rails 外掛程式是核心架構的延伸或修改。外掛程式提供

閱讀本指南後,您將知道

本指南說明如何建立測試驅動的外掛程式,該外掛程式將

為了本指南的目的,假裝你是一個狂熱的觀鳥者。你最喜歡的鳥是啄木鳥,你想要建立一個外掛程式,讓其他開發人員也能分享啄木鳥的美好。

1 設定

目前,Rails 外掛程式是建構為寶石,寶石化的外掛程式。如果需要,它們可以使用 RubyGems 和 Bundler 在不同的 Rails 應用程式中分享。

1.1 產生一個寶石化的外掛程式

Rails 內建一個 rails plugin new 指令,它會建立一個架構,用於開發任何類型的 Rails 擴充功能,並具備使用虛擬 Rails 應用程式執行整合測試的能力。使用指令建立你的外掛程式

$ rails plugin new yaffle

透過尋求協助,查看用法和選項

$ rails plugin new --help

2 測試你新產生的外掛程式

導覽到包含外掛程式的目錄,並編輯 yaffle.gemspec,以取代任何具有 TODO 值的行

spec.homepage    = "http://example.com"
spec.summary     = "Summary of Yaffle."
spec.description = "Description of Yaffle."

...

spec.metadata["source_code_uri"] = "http://example.com"
spec.metadata["changelog_uri"] = "http://example.com"

然後執行 bundle install 指令。

現在你可以使用 bin/test 指令執行測試,你應該會看到

$ bin/test
...
1 runs, 1 assertions, 0 failures, 0 errors, 0 skips

這會告訴你所有內容都已正確產生,你已準備好開始新增功能。

3 擴充核心類別

本節將說明如何新增一個方法到 String,該方法將可在你的 Rails 應用程式中的任何地方使用。

在此範例中,你將新增一個名為 to_squawk 的方法到 String。首先,建立一個新的測試檔案,並包含一些斷言

# yaffle/test/core_ext_test.rb

require "test_helper"

class CoreExtTest < ActiveSupport::TestCase
  def test_to_squawk_prepends_the_word_squawk
    assert_equal "squawk! Hello World", "Hello World".to_squawk
  end
end

執行 bin/test 以執行測試。此測試應該會失敗,因為我們尚未實作 to_squawk 方法

$ bin/test
E

Error:
CoreExtTest#test_to_squawk_prepends_the_word_squawk:
NoMethodError: undefined method `to_squawk' for "Hello World":String


bin/test /path/to/yaffle/test/core_ext_test.rb:4

.

Finished in 0.003358s, 595.6483 runs/s, 297.8242 assertions/s.
2 runs, 1 assertions, 0 failures, 1 errors, 0 skips

太棒了 - 現在你已準備好開始開發。

lib/yaffle.rb 中,新增 require "yaffle/core_ext"

# yaffle/lib/yaffle.rb

require "yaffle/version"
require "yaffle/railtie"
require "yaffle/core_ext"

module Yaffle
  # Your code goes here...
end

最後,建立 core_ext.rb 檔案,並新增 to_squawk 方法

# yaffle/lib/yaffle/core_ext.rb

class String
  def to_squawk
    "squawk! #{self}".strip
  end
end

若要測試方法是否如預期運作,請從外掛目錄執行 bin/test 來執行單元測試。

$ bin/test
...
2 runs, 2 assertions, 0 failures, 0 errors, 0 skips

若要實際測試,請切換到 test/dummy 目錄,啟動 bin/rails console,並開始發出叫聲

irb> "Hello World".to_squawk
=> "squawk! Hello World"

4 新增「acts_as」方法至 Active Record

外掛中常見的模式是將名為 acts_as_something 的方法新增至模型。在本例中,您要撰寫一個名為 acts_as_yaffle 的方法,將 squawk 方法新增至 Active Record 模型。

首先,設定檔案,以便您擁有

# yaffle/test/acts_as_yaffle_test.rb

require "test_helper"

class ActsAsYaffleTest < ActiveSupport::TestCase
end
# yaffle/lib/yaffle.rb

require "yaffle/version"
require "yaffle/railtie"
require "yaffle/core_ext"
require "yaffle/acts_as_yaffle"

module Yaffle
  # Your code goes here...
end
# yaffle/lib/yaffle/acts_as_yaffle.rb

module Yaffle
  module ActsAsYaffle
  end
end

4.1 新增類別方法

此外掛會預期您已將名為 last_squawk 的方法新增至模型。然而,外掛使用者可能已在模型中定義名為 last_squawk 的方法,用於其他用途。此外掛允許透過新增名為 yaffle_text_field 的類別方法來變更名稱。

首先,撰寫一個失敗測試,顯示您想要的行為

# yaffle/test/acts_as_yaffle_test.rb

require "test_helper"

class ActsAsYaffleTest < ActiveSupport::TestCase
  def test_a_hickwalls_yaffle_text_field_should_be_last_squawk
    assert_equal "last_squawk", Hickwall.yaffle_text_field
  end

  def test_a_wickwalls_yaffle_text_field_should_be_last_tweet
    assert_equal "last_tweet", Wickwall.yaffle_text_field
  end
end

執行 bin/test 時,您應該會看到以下訊息

$ bin/test
# Running:

..E

Error:
ActsAsYaffleTest#test_a_wickwalls_yaffle_text_field_should_be_last_tweet:
NameError: uninitialized constant ActsAsYaffleTest::Wickwall


bin/test /path/to/yaffle/test/acts_as_yaffle_test.rb:8

E

Error:
ActsAsYaffleTest#test_a_hickwalls_yaffle_text_field_should_be_last_squawk:
NameError: uninitialized constant ActsAsYaffleTest::Hickwall


bin/test /path/to/yaffle/test/acts_as_yaffle_test.rb:4



Finished in 0.004812s, 831.2949 runs/s, 415.6475 assertions/s.
4 runs, 2 assertions, 0 failures, 2 errors, 0 skips

這表示我們沒有要測試的必要模型 (Hickwall 和 Wickwall)。我們可以從 test/dummy 目錄執行下列指令,在「dummy」Rails 應用程式中輕鬆產生這些模型

$ cd test/dummy
$ bin/rails generate model Hickwall last_squawk:string
$ bin/rails generate model Wickwall last_squawk:string last_tweet:string

現在,您可以透過前往 dummy 應用程式並遷移資料庫,在測試資料庫中建立必要的資料庫表格。首先,執行

$ cd test/dummy
$ bin/rails db:migrate

在此同時,變更 Hickwall 和 Wickwall 模型,讓它們知道它們應該像 yaffle 一樣運作。

# test/dummy/app/models/hickwall.rb

class Hickwall < ApplicationRecord
  acts_as_yaffle
end
# test/dummy/app/models/wickwall.rb

class Wickwall < ApplicationRecord
  acts_as_yaffle yaffle_text_field: :last_tweet
end

我們也會新增程式碼來定義 acts_as_yaffle 方法。

# yaffle/lib/yaffle/acts_as_yaffle.rb

module Yaffle
  module ActsAsYaffle
    extend ActiveSupport::Concern

    class_methods do
      def acts_as_yaffle(options = {})
      end
    end
  end
end
# test/dummy/app/models/application_record.rb

class ApplicationRecord < ActiveRecord::Base
  include Yaffle::ActsAsYaffle

  self.abstract_class = true
end

然後你可以回到你的外掛程式根目錄 (cd ../..) 並使用 bin/test 重新執行測試。

$ bin/test
# Running:

.E

Error:
ActsAsYaffleTest#test_a_hickwalls_yaffle_text_field_should_be_last_squawk:
NoMethodError: undefined method `yaffle_text_field' for #<Class:0x0055974ebbe9d8>


bin/test /path/to/yaffle/test/acts_as_yaffle_test.rb:4

E

Error:
ActsAsYaffleTest#test_a_wickwalls_yaffle_text_field_should_be_last_tweet:
NoMethodError: undefined method `yaffle_text_field' for #<Class:0x0055974eb8cfc8>


bin/test /path/to/yaffle/test/acts_as_yaffle_test.rb:8

.

Finished in 0.008263s, 484.0999 runs/s, 242.0500 assertions/s.
4 runs, 2 assertions, 0 failures, 2 errors, 0 skips

越來越接近了... 現在我們將實作 acts_as_yaffle 方法的程式碼,讓測試通過。

# yaffle/lib/yaffle/acts_as_yaffle.rb

module Yaffle
  module ActsAsYaffle
    extend ActiveSupport::Concern

    class_methods do
      def acts_as_yaffle(options = {})
        cattr_accessor :yaffle_text_field, default: (options[:yaffle_text_field] || :last_squawk).to_s
      end
    end
  end
end
# test/dummy/app/models/application_record.rb

class ApplicationRecord < ActiveRecord::Base
  include Yaffle::ActsAsYaffle

  self.abstract_class = true
end

執行 bin/test 時,你應該會看到所有測試都通過

$ bin/test
...
4 runs, 4 assertions, 0 failures, 0 errors, 0 skips

4.2 新增一個實例方法

這個外掛程式會新增一個名為 'squawk' 的方法到任何呼叫 acts_as_yaffle 的 Active Record 物件。'squawk' 方法會單純設定資料庫中某個欄位的數值。

首先,撰寫一個失敗測試,顯示您想要的行為

# yaffle/test/acts_as_yaffle_test.rb
require "test_helper"

class ActsAsYaffleTest < ActiveSupport::TestCase
  def test_a_hickwalls_yaffle_text_field_should_be_last_squawk
    assert_equal "last_squawk", Hickwall.yaffle_text_field
  end

  def test_a_wickwalls_yaffle_text_field_should_be_last_tweet
    assert_equal "last_tweet", Wickwall.yaffle_text_field
  end

  def test_hickwalls_squawk_should_populate_last_squawk
    hickwall = Hickwall.new
    hickwall.squawk("Hello World")
    assert_equal "squawk! Hello World", hickwall.last_squawk
  end

  def test_wickwalls_squawk_should_populate_last_tweet
    wickwall = Wickwall.new
    wickwall.squawk("Hello World")
    assert_equal "squawk! Hello World", wickwall.last_tweet
  end
end

執行測試,確保最後兩個測試失敗,並出現包含 "NoMethodError: undefined method `squawk'" 的錯誤,然後更新 acts_as_yaffle.rb,讓它看起來像這樣

# yaffle/lib/yaffle/acts_as_yaffle.rb

module Yaffle
  module ActsAsYaffle
    extend ActiveSupport::Concern

    included do
      def squawk(string)
        write_attribute(self.class.yaffle_text_field, string.to_squawk)
      end
    end

    class_methods do
      def acts_as_yaffle(options = {})
        cattr_accessor :yaffle_text_field, default: (options[:yaffle_text_field] || :last_squawk).to_s
      end
    end
  end
end
# test/dummy/app/models/application_record.rb

class ApplicationRecord < ActiveRecord::Base
  include Yaffle::ActsAsYaffle

  self.abstract_class = true
end

最後執行一次 bin/test,你應該會看到

$ bin/test
...
6 runs, 6 assertions, 0 failures, 0 errors, 0 skips

使用 write_attribute 來寫入模型中的欄位,只是一個外掛程式如何與模型互動的範例,而且並不總是正確的方法。例如,你也可以使用

send("#{self.class.yaffle_text_field}=", string.to_squawk)

5 產生器

只要在你的外掛程式的 lib/generators 目錄中建立產生器,就可以將產生器包含在你的 gem 中。有關建立產生器的更多資訊,請參閱 產生器指南

6 發布你的 Gem

目前正在開發中的 Gem 外掛程式可以輕鬆地從任何 Git 儲存庫分享。要與其他人分享 Yaffle gem,只要將程式碼提交到 Git 儲存庫(例如 GitHub),並在有問題的應用程式的 Gemfile 中新增一行

gem "yaffle", git: "https://github.com/rails/yaffle.git"

執行 bundle install 之後,你的 gem 功能就會提供給應用程式使用。

當 gem 準備好要作為正式版本分享時,可以將它發布到 RubyGems

或者,你可以受益於 Bundler 的 Rake 任務。你可以使用以下指令看到完整的清單

$ bundle exec rake -T

$ bundle exec rake build
# Build yaffle-0.1.0.gem into the pkg directory

$ bundle exec rake install
# Build and install yaffle-0.1.0.gem into system gems

$ bundle exec rake release
# Create tag v0.1.0 and build and push yaffle-0.1.0.gem to Rubygems

有關在 RubyGems 中發佈寶石的更多資訊,請參閱:發佈您的寶石

7 RDoc 文件

外掛程式穩定後,您準備好部署時,請發揮善心,為它撰寫文件!幸運的是,為外掛程式撰寫文件很簡單。

第一步是更新 README 檔案,其中包含有關如何使用外掛程式的詳細資訊。幾個關鍵事項包括

  • 您的姓名
  • 如何安裝
  • 如何將功能新增到應用程式(幾個常見使用案例範例)
  • 警告、陷阱或提示,可能有助於使用者並節省他們的時間

README 檔案完善後,請瀏覽並將 RDoc 註解新增至開發人員將使用的所有方法。慣例上,也會將 # :nodoc: 註解新增至未包含在公開 API 中的程式碼部分。

註解完善後,請導覽至外掛程式目錄並執行

$ bundle exec rake rdoc

7.1 參考

意見回饋

我們鼓勵您協助提升本指南的品質。

如果您發現任何錯字或事實錯誤,請協助我們修正。若要開始,您可以閱讀我們的文件貢獻部分。

您也可能發現不完整或過時的內容。請務必新增任何遺漏的說明文件。請務必先查看Edge Guides,以驗證問題是否已在主分支中修正。查看Ruby on Rails 指南準則,了解風格和慣例。

如果您發現需要修復但無法自行修補的問題,請開啟問題

最後但同樣重要的是,歡迎在官方 Ruby on Rails 論壇上針對 Ruby on Rails 文件進行任何討論。