- 更新日: 2017年5月17日
- Rails
RailsプラグインGemの作成方法、RSpecテストまで含めたrails pluginの作り方
Rails プラグイン Gem を作る方法の紹介です。現在の Rails では、プラグインを Gem としてビルドします。通常の Ruby Gem と異なるのは、Rails プラグイン Gem は Rails のコアフレームワーク自体を拡張することができる点です。Rails 自身を拡張して機能を追加したい場合は、Rails プラグインとして Gem を作成すると良いでしょう。
通常の Ruby Gem の作り方は、以下を参照お願いいたします。
Gemの作り方(Ruby Gem) | EasyRamble
また、Rails でライブラリとして組み込むことができる gem には、通常の Ruby Gem、Rails プラグイン、Rails エンジンの3種類があります。それぞれの違いについては、以下の解説がとても分かりやすいです。
Gem、Railtieプラグイン、Engine(full/mountable)の違いとそれぞれの基礎情報 – Qiita
— 環境 —
Rails 5.1.0
Ruby 2.4.1
macOS Sierra 10.12.4
rails コマンドが使える環境を準備
Rails プラグインを作成するには、まず rails コマンドを使います。
1 2 3 |
$ rails plugin new my_plugin |
上記のように rails コマンドを使用して雛形を作成するので、rails コマンドが使える環境である必要があります。ということで、まずはその準備です。
rails コマンドをグローバルにインストール
システムの ruby や rbenv 環境で…
1 2 3 |
$ gem install rails |
とやれば、グローバルな環境で rails コマンドが使えるようになります。
rbenv 環境であれば、現在のバージョンの ruby の gem として rails が入ります。rails が利用する ActiveRecord や ActiveSupport などが全部インストールされます。これで、rails コマンドが利用できるようになります。
私は、グローバルな環境に rails をインストールするのが好みではないので、この方法は使わず以下に説明する bundler を使う方法を利用しています。
bundler で rails コマンドをインストール
もう一つの方法ですが、bundler を利用します。Rails プラグインを作成するディレクトリの vendor/bundle 以下に rails を配置して、bundle exec を通して rails コマンドを利用する方法です。以下の Rails アプリケーション作成と同様の手順となります。
Rails4 + MySQL でアプリを作成する導入部分の手順 | EasyRamble
まずは Rails プラグイン用のディレクトリを作成して bundle init。
1 2 3 4 5 |
$ mkdir my_plugin $ cd my_plugin $ bundle init |
bundle init で生成されたGemfileを編集します。
1 2 3 4 5 6 |
$ vi Gemfile source "https://rubygems.org" gem 'rails' |
vendor/bundle 以下に rails をインストール。
1 2 3 |
$ bundle install --path vendor/bundle |
これで、vendor/bundle ディレクトリに rails が入りました。ここまでで、ディレクトリ構成は以下の通り。
1 2 3 4 5 6 7 |
my_plugin | .bundle/ | vendor/ | Gemfile | Gemfile.lock |
この bundler で入れた rails コマンドを使うには、bundle exec rails ~ と利用します。私はグローバル環境に rails を入れたくないので、こちらの bundler を通して rails コマンドを利用する方法を採用しています。
確認。
1 2 3 4 |
$ bundle exec rails --version Rails 5.1.0 |
補足ですが、bundle install で nokogiri エラーが出る場合は以下等を参考お願いします。
Nokogiriインストールでlibxml2関連のエラー再発 | EasyRamble
nokogiri インストール時のエラー Running ‘patch’ for libxml2 2.8.0… ERROR | EasyRamble
El Capitanでgemのnative extensionビルド失敗に対応 | EasyRamble
以降の説明は、bundler 利用の場合で進めます。
Rails プラグインの雛形を生成
続いて、プラグイン雛形を生成します。Rails プラグイン作成用のディレクトリで作業を行う。
1 2 3 4 |
$ pwd /path/to/my_plugin |
まずは、Rails デフォルトのテストツールである Test::Unit を使う場合です。/path/to/my_plugin にいる状態なので、カレントディレクトリに雛形を生成するために rails plugin new . とドットを指定して、雛形を生成するパスをカレントディレクトリに指定する。
1 2 3 |
$ bundle exec rails plugin new . --skip-bundle |
–skip-bundle は bundle install を行わないオプション。後で Gemfile 編集後に bundle install します。
Test::Unit ではなくて RSpec を使う場合。
1 2 3 |
$ bundle exec rails plugin new . -T --skip-test-unit --skip-bundle --dummy-path=spec/dummy |
-T –skip-test-unit は Test::Unit を利用しない指定で、–dummy-path=spec/dummy はダミーの検証用 Rails アプリケーションのパス指定です。spec/dummy 以下に、検証用の Rails アプリケーションが生成されます。私は日頃から RSpec を使っていますので、こちらの RSpec 利用で雛形を生成します。
途中で Gemfile を上書きするかどうか尋ねられるので 「Y」(Yes)と入力。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
$ bundle exec rails plugin new . -T --skip-test-unit --skip-bundle --dummy-path=spec/dummy exist create README.md create Rakefile create my_plugin.gemspec create MIT-LICENSE create .gitignore conflict Gemfile Overwrite /path/to/my_plugin/Gemfile? (enter "h" for help) [Ynaqdh] Y force Gemfile create lib/my_plugin.rb create lib/tasks/my_plugin.rake create lib/my_plugin/version.rb create bin/test vendor_app spec/dummy |
これでディレクトリ構成は以下の通りになる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
my_plugin | .bundle/ | bin/ | lib/ | spec/ | vendor/ | .gitignore | Gemfile | Gemfile.lock | MIT-LICENSE | README.md | Rakefile | my_plugin.gemspec |
Gemfile と my_plugin.gemspec の確認
上書きされた Gemfile を確認します。
Gemfile
1 2 3 4 5 6 7 8 |
source 'https://rubygems.org' # Declare your gem's dependencies in my_plugin.gemspec. # Bundler will treat runtime dependencies like base dependencies, and # development dependencies will be added by default to the :development group. gemspec # ... |
rails plugin new コマンドで、Gemfile は上記のように上書きされました。gemspec の行によって、my_plugin.gemspec に記載された依存 gem が読み込まれます。
my_plugin.gemspec
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
$:.push File.expand_path("../lib", __FILE__) # Maintain your gem's version: require "my_plugin/version" # Describe your gem and declare its dependencies: Gem::Specification.new do |s| s.name = "my_plugin" s.version = MyPlugin::VERSION s.authors = ["Author Name"] s.email = ["author@example.com"] s.homepage = "TODO" s.summary = "TODO: Summary of MyPlugin." s.description = "TODO: Description of MyPlugin." s.license = "MIT" s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"] s.add_dependency "rails", "~> 5.1.0" s.add_development_dependency "sqlite3" end |
my_plugin は rails と 開発環境での sqlite3 に依存する宣言が書かれている。
Git 管理にする
このあたりで git 管理にします。まずは .gitignore を編集。
.gitignore
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
.bundle/ log/*.log pkg/ spec/dummy/db/*.sqlite3 spec/dummy/db/*.sqlite3-journal spec/dummy/log/*.log spec/dummy/tmp/ /vendor/bundle/ .DS_Store *.gem *.rbc Gemfile.lock gemfiles/*.lock coverage |
続いて Git リポジトリを初期化。
1 2 3 4 5 |
$ git init $ git add . $ git commit -m "initial commit" |
以降、ファイル編集時には適宜コミットしていきます。
my_plugin.gemspec を編集
gemspec の authors 等の情報を編集。またデフォルトで記載されている TODO や FIXME の文字列があると、spec/dummy 以下の bin/rails コマンドがエラーになるので編集する必要があります。
my_plugin.gemspec
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
$:.push File.expand_path("../lib", __FILE__) # Maintain your gem's version: require "my_plugin/version" # Describe your gem and declare its dependencies: Gem::Specification.new do |s| s.name = "my_plugin" s.version = MyPlugin::VERSION s.authors = ["Author Name"] s.email = ["author@example.com"] s.homepage = "http://example.com" s.summary = "Summary of MyPlugin." s.description = "Description of MyPlugin." s.license = "MIT" s.files = `git ls-files`.split("\n") s.test_files = `git ls-files`.split("\n").grep(%r{^(test|spec|features)/}) s.require_paths = ['lib'] s.add_dependency "rails", "~> 5.1.0" s.add_development_dependency "sqlite3" s.add_development_dependency 'byebug' s.add_development_dependency "rspec-rails" end |
デバッグ用とテスト用に、開発環境(development)での依存(add_development_dependency)に、byebug と rspec-rails を追加しました。
bundle install
gemspec および Gemfile で追記した gem をインストールするために、bundle install を実行します。
1 2 3 |
$ bundle install --path vendor/bundle |
RSpec の準備
spec 以下にテストケースを書いていくので、dummy の rails アプリケーション(spec/dummy)以下に、spec へのシンボリックリンクを作成する。
1 2 3 4 |
$ cd spec/dummy $ ln -s ../../spec |
そして、spec/dummy ディレクトリのまま rspec の雛形を生成します。
1 2 3 4 5 |
$ pwd /path/to/my_plugin/spec/dummy $ bin/rails generate rspec:install |
これでディレクトリは以下のような構造となります。
1 2 3 4 5 6 7 8 |
my_plugin/ | spec/ | | dummy/ | | | spec -> ../../spec(シンボリックリンク) | | rails_helper.rb | | spec_helper.rb |
rails_helper.rb を編集。
1 |
require File.expand_path('../../config/environment', __FILE__) |
を
1 |
require File.expand_path('../dummy/config/environment', __FILE__) |
に変更します。dummy 用 rails アプリケーションの config ファイルを読み込む設定です。
この編集が終了したら、シンボリックリンクは必要なくなるので消します。
1 2 3 4 5 |
$ pwd /path/to/my_plugin/spec/dummy $ rm spec |
RSpec を書いて動作確認
さて、ここで RSpec を書いて動作確認してみますが、せっかくですのでテスト駆動開発のスタイルで進めてみます。作成中のプラグインで、ActionView を拡張して Rails の View ヘルパーに exclamatize というメソッドを追加します。exclamatize メソッドは、文字列と指定した数を引数で渡すと、末尾に「!」を指定数だけ追加するヘルパーです。
まずは、以下のような rspec テストを書きます。
spec/controllers/application_controller_spec.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
require 'rails_helper' describe 'MyPlugin::ViewHelpers::ActionView' do it 'returns itself as is.' do expect('hello world').to eq 'hello world' end context '#exclamatize' do # 数を指定しない時は、「!」を1個追加 describe 'returns string with one exclamation mark.' do specify { expect( exclamatize('hello world') ).to eq 'hello world!' } end # 数を指定した時は、「!」を指定した数だけ追加 describe 'returns string with three exclamation mark.' do specify { expect( exclamatize('hello world', 3) ).to eq 'hello world!!!' } end end end |
テストを走らせる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
$ cd /path/to/my_plugin $ bundle exec rspec spec .FF Failures: 1) MyPlugin::ViewHelpers::ActionView #exclamatize returns string with one exclamation mark. Failure/Error: specify { expect( exclamatize('hello world') ).to eq 'hello world!' } NoMethodError: undefined method `exclamatize' for #<RSpec::ExampleGroups::MyPluginViewHelpersActionView::Exclamatize::ReturnsStringWithOneExclamationMark:0x007f8c22a53970> # ./spec/view_helpers/action_view_spec.rb:11:in `block (4 levels) in <top (required)>' 2) MyPlugin::ViewHelpers::ActionView #exclamatize returns string with three exclamation mark. Failure/Error: specify { expect( exclamatize('hello world', 3) ).to eq 'hello world!!!' } NoMethodError: undefined method `exclamatize' for #<RSpec::ExampleGroups::MyPluginViewHelpersActionView::Exclamatize::ReturnsCorrectAmpHeader:0x007f8c2386df60> # ./spec/view_helpers/action_view_spec.rb:16:in `block (4 levels) in <top (required)>' Finished in 0.01565 seconds (files took 10.63 seconds to load) 3 examples, 2 failures Failed examples: rspec ./spec/view_helpers/action_view_spec.rb:11 # MyPlugin::ViewHelpers::ActionView #exclamatize returns string with one exclamation mark. rspec ./spec/view_helpers/action_view_spec.rb:16 # MyPlugin::ViewHelpers::ActionView #exclamatize returns string with three exclamation mark. |
RSpec テストが上手く動作することを確認できましたが、テストが2つ失敗しています。これは exclamatize ヘルパーが存在しないことが原因です。以降 Rails プラグイン本体の作成で、View ヘルパー(ActionView)に exclamatize メソッドを追加する機能を作成します。
Rails プラグイン本体の作成
まずは、exclamatize ヘルパーを実装します。exclamatize メソッドは、文字列と指定した数を引数で渡すと、末尾に「!」を指定数だけ追加するヘルパーです。
lib/my_plugin/view_helpers/action_view.rb
1 2 3 4 5 6 7 8 9 10 11 |
module MyPlugin module ViewHelpers module ActionView def exclamatize(string, number = 1) string + '!' * number end ::ActionView::Base.send :include, self end end end |
「::ActionView::Base.send :include, self」の行で、Rails のコアクラスである ActionView::Base に include されます。
続いて、Railtie の機構を用いて、作成したモジュールが Rails に組み込まれるようにします。Rails::Railtie を継承したクラスで require する。
lib/my_plugin/railtie.rb
1 2 3 4 5 6 7 8 9 |
module MyPlugin class Railtie < Rails::Railtie initializer 'my_plugin' do |app| ActiveSupport.on_load :action_view do require 'my_plugin/view_helpers/action_view' end end end end |
最後に、gem ロード時のエントリーポイントとなる lib/my_plugin.rb を編集。このファイルから、lib/my_plugin/railtie.rb など gem ライブラリに必要なファイルを読み込むようにします。
lib/my_plugin.rb
1 2 3 4 5 6 7 8 9 10 |
require 'my_plugin/version' if defined?(Rails::Railtie) require 'my_plugin/railtie' else raise 'my_plugin is not compatible with Rails 2.* or older' end module MyPlugin end |
Rails コンソールとRSpec テストで動作確認
dummy の rails アプリケーションでコンソールを起動し、動作確認してみます。
1 2 3 4 5 6 7 8 |
$ cd spec/dummy $ bin/rails c irb(main):002:0> helper.exclamatize('hello world') => "hello world!" irb(main):004:0> helper.exclamatize('hello world', 3) => "hello world!!!" |
上手く動いているようです。
また RSpec でもテスト実行を確認してみます。作成したモジュールを rails_helper から読み込むようにします。
spec/rails_helper.rb
1 |
config.include MyPlugin::ViewHelpers::ActionView |
テスト実行。
1 2 3 4 5 6 7 |
$ bundle exec rspec spec ... Finished in 0.0137 seconds (files took 6.01 seconds to load) 3 examples, 0 failures |
今度は RSpec テストが全部パスしました。以上で、ひと通り Rails プラグインの作成は終了です。
RubyGems に公開する
Ruby Gem として登録するためには、RubyGems(RubyGems.org)リポジトリに公開する必要があります。Ruby Gem 開発の場合、開発用リポジトリを GitHub にして、リリース時に rubygems.org に公開というフローが多いです。私もその運用にしています。
rubygems.org に公開するためには、まずビルドして公開用のパッケージを作ります。
1 2 3 |
$ bundle exec rake build |
RubyGems の API キーを取得。事前に、rubygems.org のアカウント取得が必要です。
1 2 3 4 |
$ curl -u username https://rubygems.org/api/v1/api_key.yaml > ~/.gem/credentials; chmod 0600 ~/.gem/credentials Enter host password for user 'username' |
取得した API キーを確認します。
1 2 3 |
$ cat ~/.gem/credentials |
最後に rubygems.org に公開です。
1 2 3 |
$ bundle exec rake release |
あとは、開発を行って開発用リポジトリ(GitHubなど)にプッシュ〜正式リリース(rubygems.orgに公開)を繰り返していきます。rubygems.org にリリース(アップデート)する場合は、バージョン番号を上げる必要があることに注意です。以前と同じバージョン番号だと弾かれてしまいます。
以上、RailsプラグインGemの作成方法でした。おつかれさまでした!なお、今回作成したサンプル用の Rails プラグイン Gem を GitHub に上げていますので、よろしかったらこちらもご参考ください。
takafumir/my_plugin: Sample for Rails plugin gem. MyPlugin adds exclamatize
helper to ActionView.
- – 参考リンク –
- Rails プラグイン作成入門 | Rails ガイド
- Rails用のgemを作成する手順 (Rails 4.0以降) Oh My Enter!
- Rails pluginのテストでRspecを使う | 69log
- Rails の関連記事
- RailsでMySQLパーティショニングのマイグレーション
- Rails ActiveRecordでdatetime型カラムのGROUP BY集計にタイムゾーンを考慮する
- RailsでAMPに対応するgemをリリースしました
- Railsでrequest.urlとrequest.original_urlの違い
- Railsでwheneverによるcronバッチ処理
- Google AnalyticsのRails Turbolinks対応
- Railsアプリにソーシャル・シェアボタンを簡単設置
- Rails監視ツール用にErrbitをHerokuで運用
- Facebook APIバージョンのアップグレード手順(Rails OmniAuth)
- window.NREUMがHTMLヘッダー部に自動挿入されるのはNew Relic用
- 初回公開日: 2017年5月12日
Leave Your Message!