Web Marina

日々の業務や勉強などで得た知識をアウトプットしていきます。

Rails プラグインを学んでみる。(後編)

f:id:song-of-life1352607:20170411225447p:plain

こんにちは、マリンです。


前回はRailsガイドの「プラグイン作成入門」の3まで書きました。今回は続きの4から書きます。


前回の記事はこちら。

song-of-life.hatenablog.com

4.“acts_as"メソッドをActive Recordに追加する。

acts_as~

  • プラグインのモデルによく追加されるメソッドの名前。


ここでは後に作成する"squawk"メソッドをActive Recordに追加するためのメソッド、"acts_as_yaffle"を追加します。


<必要ファイルの準備>

   # test/acts_as_yaffle_test.rb
        require 'test_helper'
         
        class ActsAsYaffleTest < ActiveSupport::TestCase
        end
        
    # lib/yaffle.rb
        require 'yaffle/core_ext'
        require 'yaffle/acts_as_yaffle'
         
        module Yaffle
        end
        
         # lib/yaffle/acts_as_yaffle.rb
        module Yaffle
          module ActsAsYaffle
            # ここにコードを書く
          end
        end
    # test/acts_as_yaffle_test.rb
        require 'test_helper'
         
        class ActsAsYaffleTest < ActiveSupport::TestCase
        end
        
    # lib/yaffle.rb
        require 'yaffle/core_ext'
        require 'yaffle/acts_as_yaffle'
         
        module Yaffle
        end
        
    # lib/yaffle/acts_as_yaffle.rb
        module Yaffle
          module ActsAsYaffle
            # ここにコードを書く
          end
        end

クラスメソッドを追加する

プラグインはホストアプリにインストールして使うものです。 そのため、もしかするとホストアプリとプラグインでメソッド名が被ってしまうことが起こるかもしれません。その問題を解決するためのクラスメソッドを追加します。

  • “yaffle_text_field” : 名前を変更できるようにするメソッド。


<test作成>

# 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

このテストはHickwallとWickwallモデルがないのでRedです。


<モデル作成>

テストを通すために必要なモデルをダミーアプリに作成します。

$ 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

作成したモデルがyafflesとして振る舞うよう指示します。

# test/dummy/app/models/hickwall.rb
 
class Hickwall < ActiveRecord::Base
  acts_as_yaffle
end
 
# test/dummy/app/models/wickwall.rb
 
class Wickwall < ActiveRecord::Base
  acts_as_yaffle yaffle_text_field: :last_tweet
end




<acts_as_yaffleメソッドを定義>

前述のモデルに使用したacts_as_yaffleを定義します。

# yaffle/lib/yaffle/acts_as_yaffle.rb
module Yaffle
  module ActsAsYaffle
    extend ActiveSupport::Concern
 
    included do
    end
 
    module ClassMethods
      def acts_as_yaffle(options = {})
        # ここにコードを書く
      end
    end
  end
end
 
ActiveRecord::Base.send :include, Yaffle::ActsAsYaffle




ここでテストを行うと、今度はyaffle_text_fieldが定義されていないということでRedになると思います。前述のモデルに使用したacts_as_yaffleメソッドを実装し、この中で定義してテストが通るようにします。

# yaffle/lib/yaffle/acts_as_yaffle.rb
 
module Yaffle
  module ActsAsYaffle
   extend ActiveSupport::Concern
 
    included do
    end
 
    module ClassMethods
      def acts_as_yaffle(options = {})
        cattr_accessor :yaffle_text_field
        self.yaffle_text_field = (options[:yaffle_text_field] || :last_squawk).to_s
      end
    end
  end
end
 
ActiveRecord::Base.send :include, Yaffle::ActsAsYaffle

cattr_accessor

  • クラス外から対象のクラス変数へアクセスするためのメソッドです。
  • 似たものにattr_accessorがありますが、これはインスタンス変数にアクセスするためのメソッドです。

これでテストはGreenになります。

インスタンスメソッドを追加する

先に述べた'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

squawkが定義されていないのでこれはRedになります。


<'squawk'を定義>

# yaffle/lib/yaffle/acts_as_yaffle.rb
 
module Yaffle
  module ActsAsYaffle
    extend ActiveSupport::Concern
 
    included do
    end
 
    module ClassMethods
      def acts_as_yaffle(options = {})
        cattr_accessor :yaffle_text_field
        self.yaffle_text_field = (options[:yaffle_text_field] || :last_squawk).to_s
 
        include Yaffle::ActsAsYaffle::LocalInstanceMethods
      end
    end
 
    module LocalInstanceMethods
      def squawk(string)
        write_attribute(self.class.yaffle_text_field, string.to_squawk)
      end
    end
  end
end
 
ActiveRecord::Base.send :include, Yaffle::ActsAsYaffle

これでテストはGreenになります。


ここまででプラグインの開発は終了です。

5. ジェネレータ

ジェネレータ

  • 与えられた条件などに基づいてデータやプログラムコードを自動的に生成するプログラムなど。


    もしgemにジェネレータを含めたい場合には、作成したジェネレータをプラグインlib/generatorsディレクトリに置くだけで大丈夫です。

6. gemを公開する

せっかく作成したプラグインなので、公開したい場合があると思います。gemはGitリポジトリを使用して共有が可能です。


他のアプリケーションで使用するには、まずgemのコードをリモートリポジトリにコミットし、その後gemを使用したいアプリケーションのGemfileに書いてbundle installすることで利用できます。

gem 'yaffle', git: 'git://github.com/yaffle_watcher/yaffle.git'

この時コードのURLや、ローカルのディレクトリの場合はパスを忘れず書きましょう。


もしgemを正式なリリースとして一般公開するなら、RubyGemsでパブリッシュします。詳細はこちら(英語です。)


ここまでで直接開発に関わる説明は終わりです。 7項は「デプロイするならきちんとドキュメントを書きましょう」という内容なので省略します。

まとめ

基本的にはアプリの開発と同じですね。あとはファイル構成や、gemspec、本アプリでの使用方法などでつまずかなければいけそうです。


私の場合は自分のサイトにブログ機能を追加することが目的なので、よりホストアプリに寄り添ってるっぽいエンジン(多分似たようなものだと思うのですが)を使って開発していこうと思います。


とはいえ、プラグインが作れるようになるとできることの幅が広がりそうなので、サラッとでも勉強しておくと良さそうですね。