読者です 読者をやめる 読者になる 読者になる

CA MOBILE エンジニアブログ

株式会社シーエー・モバイルのエンジニアブログです

Rails gem を作ろう 1 - Rails プラグインを作る -

Rails Plugins Ruby

はじめまして、メディア事業部のフォンと申します。今回は Railsプラグインを作る方法を紹介致します。

サービスを開発するとき Rails だけで全部出来ますよね?なぜプラグインまで作らなければならないのでしょうか?

その答えはDRYです。

Don't Repeat Yourself(DRY)「同じことを繰り返さない」という開発パターンを実現するために Rails プラグインという仕組みが大きく役に立ちます。

例えば、大抵のシステムでユーザーテーブルを必要とすると思います。開発対象のサービスはたった一つだけなら問題無いですが、もしチーム内で2つ以上のサービスを開発するようになったら、そこでユーザーテーブルの実装の繰り返しが始まります。それを避けるために Railsプラグインシステムが実現されてあります。

今回は例として(つまらないですが)HelloJapanプラグインを作ります。 注意事項が一つあります。今回作るプラグインは普通の Ruby gem ではなく、Railsプラグインです。間違わないように気をつけてください。 じゃ…早速やってみましょう。

初期化コマンドは下記の通り。

rails plugin new HelloJapan

すると…

      create
      create  README.rdoc
      create  Rakefile
      create  hello_japan.gemspec
      create  MIT-LICENSE
      create  .gitignore
      create  Gemfile
      create  lib/hello_japan.rb
      create  lib/tasks/hello_japan_tasks.rake
      create  lib/hello_japan/version.rb
      create  test/test_helper.rb
      create  test/hello_japan_test.rb
      append  Rakefile
  vendor_app  test/dummy
         run  bundle install

が実行されbundle installの前まで準備してくれます。

では今回の一番重要なファイルを弄ってみましょう。 hello_japan.gemspecファイルですね。 中身をみると…

$:.push File.expand_path("../lib", __FILE__)

# Maintain your gem's version:
require "hello_japan/version"

# Describe your gem and declare its dependencies:
Gem::Specification.new do |s|
  s.name        = "hello_japan"
  s.version     = HelloJapan::VERSION
  s.authors     = ["Phone Phone"]
  s.email       = ["phonephone@example.com"]
  s.homepage    = "TODO"
  s.summary     = "TODO: Summary of HelloJapan."
  s.description = "TODO: Description of HelloJapan."
  s.license     = "MIT"

  s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.rdoc"]
  s.test_files = Dir["test/**/*"]

  s.add_dependency "rails", "~> 4.2.3"

  s.add_development_dependency "sqlite3"
end

まず、TODOの部分を全部直して、Ruby バージョンを指定します。

#coding: utf-8
$:.push File.expand_path("../lib", __FILE__)

# Maintain your gem's version:
require "hello_japan/version"

# Describe your gem and declare its dependencies:
Gem::Specification.new do |s|
  s.name        = "hello_japan"
  s.version     = HelloJapan::VERSION
  s.authors     = ["Phone Phone"]
  s.email       = ["phonephone@example.com"]
  s.homepage    = "http://www.hello-japan.com"
  s.summary     = "Stringクラスにhello_japanヘルパーメソードを追加します。"
  s.description = "NilClassにもhello_japanを追加して文字列がnilになってしまった場合サービスが落ちないようにします。"
  s.license     = "MIT"

  s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.rdoc"]
  s.test_files = Dir["test/**/*"]

  s.add_dependency "rails", "~> 4.2.3"
  s.required_ruby_version "~> 2.2.2"

  s.add_development_dependency "sqlite3"
end

あらかじめ test/hello_japan_test.rb の中に欲しい機能をお願いしておきます。

#coding: utf-8
require 'test_helper'

class HelloJapanTest < ActiveSupport::TestCase
  test "truth" do
    assert_kind_of Module, HelloJapan
  end

  test "String has hello_japan method" do
    assert_equal "Hello JapanはHello Japanになります", ":hj:はHello Japanになります".hello_japan
  end

  test "Nil has hello_japan method" do
    assert_nil nil.hello_japan
  end
end

それでrake testを実行してみると、もちろん失敗してしまいます。直しましょう。

vim lib/hello_japan/core_ext.rbで新しいファイルを開いて、下記のコードを書きます。

String.class_eval do
  def hello_japan
    gsub(/:hj:/,"Hello Japan")
  end
end

NilClass.class_eval do
  def hello_japan
    nil
  end
end

もう一度rake testをすると、まだエラーが発生します。 なぜなら、そのcore_ext.rbファイルが自動でrequireされてないからです。 lib/hello_japan.rbの中に

require "hello_japan/core_ext"

を入れて下さい。 今度はrake testが通りました。

# Running:

...

Finished in 0.003512s, 854.1497 runs/s, 854.1497 assertions/s.

念のため、コンソールで確かめてみましょう。 Rails プラグインはテストの為、ダミーアプリが作られますので、

cd test/dummy

rails console

に入ります。そして、コンソールで下記を打つと

> "Good morning. :hj:.".hello_japan

結果が

 => "Good morning. Hello Japan."

になります。 nilも問題無いです。

> nil.hello_japan
 => nil

今回はここで終了致します。

次の機会があったら、ActiveRecordを拡張する方法を紹介します。

幸せな一日を…