Rails Plugin ことはじめ

Rails Advent Calendar 4 日目の記事です。前の日は [yaotti](http://qiita.com/users/yaotti) さんの [rails-shを使って高速railsサブコマンド & rakeタスク実行](http://qiita.com/items/adfa3ffb4a5c3783d2d0) でした。こんなんあったんだ。すげぇ、便利。

## 目次
1. 素のプラグインを作る
2. ちょこちょこいじってみる
3. RSpec でテストしたい
4. RSpec でテスト(2週目)

# 素のプラグインを作る

なにはともあれとりあえず簡単なのはひな形を作ってしまうことです。

```
$ rails plugin new test_plugin
```

何も動きませんが、とにかくものは出来ました。テストもすでにできています。

```
$ cd test_plugin
$ bundle exec rake test
Run options: 

# Running tests:

.

Finished tests in 0.577092s, 1.7328 tests/s, 1.7328 assertions/s.

1 tests, 1 assertions, 0 failures, 0 errors, 0 skips
```

テストが1つ通っています。

あとは基本的に普通の gem と同じように開発していくだけです。

ActiveRecord の拡張や、 ActiveSupport の拡張などは公式の [Ruby on Rails Guides: The Basics of Creating Rails Plugins](http://guides.rubyonrails.org/plugins.html) が参考になります。

# ちょこちょこいじってみる

さて、せっかくなので何か作ってみようかとも思うのですが、なにを作ったものか……

うまく思いつかなかったので、自作の [mount_doc](https://rubygems.org/gems/mount_doc)([Github](https://github.com/rosylilly/mount_doc)) という簡単な Rails Plugin を参考に話を進めます。

とはいえ、いろいろやっているので、簡単に解説……というのは難しいのですが、さっくり言ってしまえば `Rails::Engine` をいじっているというしかありません。

実際の Rails アプリケーションも `Rails::Engine` を継承した `Rails::Application` クラスになっていたりするのですが、`Rails::Engine` を継承したクラスを用意することで、 Rails アプリケーションにマウントできるようになります。

結構フィーリングで書いたり、作った時に調べた資料を失念していたりで詳しく解説出来ないので、ここはまた今度、 `Rails::Engine` の歩き方とでも題してブログにまとめようかとおもいます。

# RSpec でテストしたい

標準のテストは `TestUnit` でできており、RSpec のぬるま湯になれきった僕にはちょっと辛いものがあります。

標準のテストは `test/dummy` 以下にダミーの Rails アプリケーションが配置されており、便利にテストをすることが出来るのですが、今回は RSpec を使いたいので使いません。

TestUnit を使わずに RSpec でテストをする場合は、プラグインを作成する際にコマンドライン引数を足します。

```
$ rails plugin new rspec_test_plugin -T
```

これで TestUnit を含まないプラグインのひな形が完成します。

次は Gemfile いじり。RSpec を追加します。

```diff
diff --git rspec_test_plugin.gemspec rspec_test_plugin.gemspec
index b5933ad..a9653bf 100644
--- rspec_test_plugin.gemspec
+++ rspec_test_plugin.gemspec
@@ -18,4 +18,5 @@ Gem::Specification.new do |s|
   s.add_dependency "rails", "~> 3.2.8"
 
   s.add_development_dependency "sqlite3"
+  s.add_development_dependency "rspec"
 end
```

編集したら次のコマンド

```
$ bundle install
$ bundle exec rspec --init
```

これで `spec/spec_helper.rb` と `.rspec` が出来ました。あとはいつもするようにテストを書くだけです。

# RSpec でテストしたい(2週目)

標準の TestUnit には Rails アプリがついてるのに RSpec だとついてない！テストしづらい！というあなた向け。

何のことはない、普通の Rails アプリケーションをテストするくらいの気楽さでやればいいのです。

どんどん行きましょう。

```
$ mkdir -p spec/dummy
$ cd spec/dummy
$ rails new "app_$(bundle exec rails -v | sed -e 's/Rails //')" -T
```

さて、これで `app_3.2.8` というフォルダの中にテストがない Rails アプリができたはずです(3.2.8 はお使いの Rails のバージョンに適宜読み替えてください)。

なぜ Rails のバージョンをわざわざつけたかは後で解説します。

お次は `rspec_test_plugin.gemspec` です。

```diff
diff --git a/rspec_test_plugin.gemspec b/rspec_test_plugin.gemspec
index b5933ad..fe455db 100644
--- a/rspec_test_plugin.gemspec
+++ b/rspec_test_plugin.gemspec
@@ -18,4 +18,6 @@ Gem::Specification.new do |s|
   s.add_dependency "rails", "~> 3.2.8"
 
   s.add_development_dependency "sqlite3"
   s.add_development_dependency "rspec"
+  s.add_development_dependency "rspec-rails"
 end
```

足したら

```
$ bundle install
```

次は `spec/spec_helper.rb` を編集します。

```diff
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index dbc4f1a..d54b6ba 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -4,6 +4,17 @@
 # loaded once.
 #
 # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
+
+ENV['RAILS_VERSION'] ||= `bundle exec rails -v | sed -e 's/Rails //g'`.strip
+ENV['RAILS_VERSION']
+
+ENV['RAILS_ENV'] ||= 'test'
+require File.expand_path(
+  "../dummy/app_#{ENV['RAILS_VERSION']}/config/environment",
+  __FILE__)
+require 'rspec/rails'
+require 'rspec/autorun'
+
 RSpec.configure do |config|
   config.treat_symbols_as_metadata_keys_with_true_values = true
   config.run_all_when_everything_filtered = true
@@ -14,4 +25,6 @@ RSpec.configure do |config|
   # the seed, which is printed after each run.
   #     --seed 1234
   config.order = 'random'
+
+  config.include ActiveSupport::Testing::Assertions
 end
```

こうしておくことで環境変数 `RAILS_VERSION` を切り替えれば複数の Rails でテストを動かす、といった器用な真似ができるようになります。 Travis-CI などで動かすときによいと思います。

最後に `spec/dummy/app_3.2.8/Gemfile` の編集です。

```diff
diff --git a/spec/dummy/app_3.2.8/Gemfile b/spec/dummy/app_3.2.8/Gemfile
index 5367a9a..f186f3c 100644
--- a/spec/dummy/app_3.2.8/Gemfile
+++ b/spec/dummy/app_3.2.8/Gemfile
@@ -2,6 +2,8 @@ source 'https://rubygems.org'
 
 gem 'rails', '3.2.8'
 
+gemspec path: File.expand_path('../../../../', __FILE__)
+
 # Bundle edge Rails instead:
 # gem 'rails', :git => 'git://github.com/rails/rails.git'
```

開発中の gem を読み込ませます。手っ取り早いので gemspec を食わせています。

あとは普通の Rails アプリケーションを RSpec でテストするように書けば万事オーライです。

# おわりに

Rails のモンキーパッチを当てたり、共通化できそうな機能や、アウトポートできそうな便利ヘルパーの開発などもあるかとは思います。そんな時に Gem にして公開してしまうのはいいことですし、公開しなくてもこれで Rails Plugin に興味を持っていただいて、

__「もしや、これは Rails Plugin 化可能なのでは……？」__

などと、常日頃から虎視眈々と機会を探してコードを書くことで、分離しやすい疎結合なコードが書けるようになったりしないかなぁ、などと楽観的なことを考える次第です。

日本人作の Rails Plugin は結構少ない(僕観測)ので、今作れば一躍スターの座に踊り出ることが出来るかもしれません！そうでなくとも会社で Rails を使っているなら、複雑怪奇な Rails::Engine を読み解いた人は重宝されるはずです！

しあわせな Rails Plugin タイムを！