1 設置
目前,Rails插件是作為gem構建的,即gemified plugins。如果需要,可以使用RubyGems和Bundler在不同的Rails應用程序之間共享它們。
1.1 生成一個gem化的插件
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應用程序中任何地方都可用的方法。
在此示例中,您將向String添加一個名為to_squawk
的方法。首先,創建一個帶有幾個斷言的新測試文件:
# 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 向Active Record添加"acts_as"方法
插件中的一個常見模式是向模型添加一個名為acts_as_something
的方法。在這種情況下,您希望編寫一個名為acts_as_yaffle
的方法,該方法將向Active Record模型添加一個squawk
方法。
首先,設置您的文件,以便擁有以下內容:
# 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)。我們可以通過在我們的「虛擬」Rails應用程式中運行以下命令來輕鬆生成這些模型:
$ cd test/dummy
$ bin/rails generate model Hickwall last_squawk:string
$ bin/rails generate model Wickwall last_squawk:string last_tweet:string
現在,你可以通過進入你的虛擬應用程式並遷移數據庫來在測試數據庫中創建必要的數據表。首先,運行:
$ cd test/dummy
$ bin/rails db:migrate
同時,修改 Hickwall 和 Wickwall 模型,讓它們知道它們應該像 yaffles 一樣運作。
# 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 新增一個實例方法
這個插件將在調用 acts_as_yaffle
的任何 Active Record 物件中添加一個名為 'squawk' 的方法。'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
方法在模型中寫入字段只是插件與模型交互的一個示例,並不總是適用的方法。例如,你也可以使用:
ruby
send("#{self.class.yaffle_text_field}=", string.to_squawk)
5 產生器
在你的插件中,你可以通過在 lib/generators
目錄中創建它們來包含產生器。有關產生器創建的更多信息,請參閱產生器指南。
6 發布你的寶石
目前正在開發中的寶石插件可以從任何 Git 存儲庫中輕鬆共享。要與其他人共享 Yaffle 寶石,只需將代碼提交到 Git 存儲庫(如 GitHub),並在相應應用程序的 Gemfile
中添加一行:
gem "yaffle", git: "https://github.com/rails/yaffle.git"
運行 bundle install
後,你的寶石功能將對應用程序可用。
當寶石準備好作為正式版本共享時,可以將其發布到RubyGems。
或者,你可以從 Bundler 的 Rake 任務中受益。你可以使用以下命令查看完整列表:
$ bundle exec rake -T
$ bundle exec rake build
# 將 yaffle-0.1.0.gem 构建到 pkg 目錄中
$ bundle exec rake install
# 將 yaffle-0.1.0.gem 构建並安裝到系統寶石中
$ bundle exec rake release
# 創建標籤 v0.1.0,並將 yaffle-0.1.0.gem 构建並推送到 Rubygems
有關將寶石發布到 RubyGems 的更多信息,請參閱:發布你的寶石。
7 RDoc 文檔
一旦你的插件穩定下來,並且你準備部署,請幫助其他人編寫文檔!幸運的是,為插件編寫文檔很容易。
第一步是使用詳細信息更新 README 文件,說明如何使用你的插件。一些關鍵的事項包括:
- 你的名字
- 如何安裝
- 如何將功能添加到應用程序中(幾個常見用例的示例)
- 可能幫助用戶並節省時間的警告、注意事項或提示
一旦你的 README 文件完善,請遍歷並為開發人員將使用的所有方法添加 RDoc 註釋。通常還會在不包含在公共 API 中的代碼部分添加 # :nodoc:
註釋。
一旦你的註釋準備就緒,切換到插件目錄並運行:
$ bundle exec rake rdoc
7.1 參考資料
回饋
歡迎協助提升本指南的品質。
如果您發現任何錯別字或事實錯誤,請貢獻您的力量。 開始之前,您可以閱讀我們的 文件貢獻 部分。
您也可能會發現不完整的內容或過時的資訊。 請為主要的文件補充任何遺漏的內容。請先檢查 Edge 指南,以確認問題是否已經修復或尚未在主分支上修復。 請參考 Ruby on Rails 指南指引 以了解風格和慣例。
如果您發現需要修復但無法自行修補的問題,請 開啟一個問題。
最後但同樣重要的是,關於 Ruby on Rails 文件的任何討論都非常歡迎在 官方 Ruby on Rails 論壇 上進行。