1 设置
目前,Rails插件被构建为gem,即gemified plugins。如果需要,可以使用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)。我们可以通过从“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
将字段写入模型中只是插件与模型交互的一种示例,并不总是适合使用的正确方法。例如,您还可以使用:
ruby
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
# 将 yaffle-0.1.0.gem 构建到 pkg 目录中
$ bundle exec rake install
# 将 yaffle-0.1.0.gem 构建并安装到系统 gem 中
$ bundle exec rake release
# 创建标签 v0.1.0,并将 yaffle-0.1.0.gem 构建并推送到 Rubygems
有关将 gem 发布到 RubyGems 的更多信息,请参阅:发布您的 gem。
7 RDoc 文档
一旦您的插件稳定下来,并且您准备部署,为其他人提供帮助并为其编写文档!幸运的是,为插件编写文档很容易。
第一步是使用详细信息更新 README 文件,说明如何使用您的插件。包括以下几点是很重要的:
- 您的姓名
- 如何安装
- 如何将功能添加到应用程序(常见用例的几个示例)
- 可能有助于用户并节省时间的警告、注意事项或提示
一旦您的 README 文件完善,继续并为开发人员将使用的所有方法添加 RDoc 注释。通常还会在不包含在公共 API 中的代码部分添加 # :nodoc:
注释。
一旦您的注释准备就绪,请导航到插件目录并运行:
$ bundle exec rake rdoc
7.1 参考资料
反馈
欢迎您帮助改进本指南的质量。
如果您发现任何拼写错误或事实错误,请贡献您的意见。 要开始,请阅读我们的 文档贡献 部分。
您还可能会发现不完整的内容或过时的内容。 请为主要内容添加任何缺失的文档。请先检查 Edge 指南,以验证问题是否已经修复或尚未修复。 请参阅 Ruby on Rails 指南准则 以了解样式和规范。
如果您发现需要修复但无法自行修复的问题,请 提交问题。
最后但同样重要的是,欢迎您在 官方 Ruby on Rails 论坛 上讨论有关 Ruby on Rails 文档的任何问题。