1 安装
目前,Rails 插件作为 gem 构建,即 gemified 插件。如果需要,它们可以使用 RubyGems 和 Bundler 在不同的 Rails 应用程序之间共享。
1.1 生成一个 Gemified 插件
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),而我们正在尝试对其进行测试。我们可以通过从 test/dummy
目录运行以下命令轻松地在“虚拟”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
向模型中的字段写入只是插件与模型交互的一种方式,并不总是最佳方法。例如,您还可以使用
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
有关将 gem 发布到 RubyGems 的更多信息,请参阅:发布您的 gem。
7 RDoc 文档
一旦您的插件稳定并且您准备部署,请为每个人提供帮助并对其进行文档化!幸运的是,为您的插件编写文档很容易。
第一步是更新 README 文件,其中包含有关如何使用插件的详细说明。一些关键要点包括
- 您的姓名
- 如何安装
- 如何将功能添加到应用程序(一些常见用例示例)
- 可能有助于用户并节省他们时间的警告、注意事项或技巧
一旦您的 README 文件完备,请浏览并向开发者将使用的所有方法添加 RDoc 注释。按照惯例,还要向不包含在公共 API 中的代码部分添加 # :nodoc:
注释。
一旦您的注释准备就绪,请导航到您的插件目录并运行
$ bundle exec rake rdoc