#author("2017-03-14T14:00:55+09:00","default:wikiwriter","wikiwriter") #author("2017-03-14T14:07:32+09:00","default:wikiwriter","wikiwriter") &tag(Rails/プラグイン/チュートリアル); *目次 [#r8006278] #contents *関連ページ [#k731fe49] -[[Rails/プラグイン]] *参考情報 [#beef44fa] -https://railsguides.jp/engines.html -以下Rails 4.2の場合で説明。railsguidesはその時点でのRails最新版で書き直されているが過去のRailsに対応したバージョンもトップページから辿ることができる。 *エンジンプロジェクトの作成 [#w6819aae] -以下のコマンドを実行する(rails-factoryディレクトリで実行)。 bundle exec rails plugin new ~/work/blorgh --mountable --skip-bundle - "~/work/blorgh"ディレクトリに移動。 -blorgh.gemspecを編集してTODOを削除。 -bundle installを実行 bundle install --path=vendor/bundle ''ポイント'' -"--mountable"オプションを使用すると、ApplicationControllerやApplicationHelperが名前空間化される。ところが"-full"と違う。 -ダミーのテスト用アプリケーションが test/dummyに配置される。ダミーアプリケーションのルーティングファイルは、test/dummy/config/routes.rbとなる。 **エンジンの構成 [#c9d1c344] -blorgh.gemspec。エンジンのルートディレクトリに存在。利用側から以下のように使う。 #pre{{ gem 'blorgh', path: "vendor/engines/blorgh" }} -アプリケーションあらはlib/blorgh.rbが読み込まれる。ここにプラグインごとの設定を記述するのもよい。 #pre{{ require "blorgh/engine" module Blorgh end }} -エンジンの基本ファイルはlib/blorgh/engine.rbのなかにある。 #pre{{ module Blorgh class Engine < ::Rails::Engine isolate_namespace Blorgh end end }} -これによってエンジンがRailsアプリにマウントされる。 -isolate_namespaceは名前空間の分離に重要。 -appディレクトリは通常のRailsアプリと同じ構成。 -testディレクトリはエンジンのテストを行うための場所。 -test/dummyにテスト用のRailsアプリが存在。このアプリはtest/dummy/config/routes.rbで以下のようにマウントしている。 #pre{{ Rails.application.routes.draw do mount Blorgh::Engine => "/blorgh" end }} -test/integrationに統合テスト。test/modelsを作成しても良い。 *エンジン機能を提供する [#wf8f5949] **Articleリソースの作成 [#n9ae1e08] -scaffoldを実行。ネームスペース"blorgh"が追加されていることに注意。 #pre{{ $ bundle exec rails generate scaffold article title:string text:text invoke active_record create db/migrate/20170313042107_create_blorgh_articles.rb create app/models/blorgh/article.rb invoke test_unit create test/models/blorgh/article_test.rb create test/fixtures/blorgh/articles.yml invoke resource_route route resources :articles invoke scaffold_controller create app/controllers/blorgh/articles_controller.rb invoke erb create app/views/blorgh/articles create app/views/blorgh/articles/index.html.erb create app/views/blorgh/articles/edit.html.erb create app/views/blorgh/articles/show.html.erb create app/views/blorgh/articles/new.html.erb create app/views/blorgh/articles/_form.html.erb invoke test_unit create test/controllers/blorgh/articles_controller_test.rb invoke helper create app/helpers/blorgh/articles_helper.rb invoke test_unit invoke assets invoke js create app/assets/javascripts/blorgh/articles.js invoke css create app/assets/stylesheets/blorgh/articles.css invoke css create app/assets/stylesheets/scaffold.css }} -config/routes.rbが以下のように変更となる。 #pre{{ Blorgh::Engine.routes.draw do resources :articles end }} -コントローラー、ビュー、ヘルパーなども名前空間に分離されている。 -scaffoldで作成されたリソースファイルは、エンジンに適用されない。 -マイグレーション実行(Webサイトのようにbin/rails ではなくrakeでは?) bundle exec rake db:migrate -test/dummyディレクトリでrailsアプリが起動できるようになる。 bundle exec rails s -次のURLにアクセス。http://localhost:3000/blorgh/articles -エンジンルート/config/routes.rbに以下を追加 root to: "articles#index" -すると http://localhost:3000/blorgh/ でアクセスできる。 **commentsリソースを作成する [#h34a456a] ***モデルの作成 [#dc6d7ff1] -エンジンのルートディレクトリでモデルのジェネレータを実行する。 #pre{{ $ bundle exec bin/rails generate model Comment article_id:integer text:text invoke active_record create db/migrate/20170313044114_create_blorgh_comments.rb create app/models/blorgh/comment.rb invoke test_unit create test/models/blorgh/comment_test.rb create test/fixtures/blorgh/comments.yml }} -マイグレーション実行。 bundle exec rake db:migrate -ビューの変更。app/views/blorgh/articles/show.html.erbを編集。Editリンクの前に以下を追加。 #pre{{ <h3>Comments</h3> <%= render @article.comments %> }} -app/models/blorgh/article.rbに追加。 #pre{{ has_many :comments }} -記事を作成するためのフォームを作成。app/views/blorgh/articles/show.html.erbのrender @article.comments呼び出しの直後に以下の行を追加。 #pre{{ <%= render "blorgh/comments/form" %> }} -app/views/blorgh/commentsディレクトリに_form.html.erbを作成。 #pre{{ <h3>New comment</h3> <%= form_for [@article, @article.comments.build] do |f| %> <p> <%= f.label :text %><br> <%= f.text_area :text %> </p> <%= f.submit %> <% end %> }} -コメント用のルーティングを追加。config/routes.rbを編集。 #pre{{ resources :articles do resources :comments end }} ***コントローラーの作成 [#l31af821] -scaffoldの実行。 #pre{{ $ bundle exec rails g controller comments Expected string default value for '--helper'; got true (boolean) Expected string default value for '--assets'; got true (boolean) create app/controllers/blorgh/comments_controller.rb invoke erb exist app/views/blorgh/comments invoke test_unit create test/controllers/blorgh/comments_controller_test.rb invoke helper create app/helpers/blorgh/comments_helper.rb invoke test_unit invoke assets invoke js create app/assets/javascripts/blorgh/comments.js invoke css create app/assets/stylesheets/blorgh/comments.css }} -app/controllers/blorgh/comments_controller.rbを編集。ネストリソースの場合メインオブジェクトのidがパラメータとして(article_idとして)渡されてくる。 #pre{{ require_dependency "blorgh/application_controller" module Blorgh class CommentsController < ApplicationController def create @article = Article.find(params[:article_id]) @comment = @article.comments.create(comment_params) flash[:notice] = "Comment has been created!" redirect_to articles_path end private def comment_params params.require(:comment).permit(:text) end end end }} -app/views/blorgh/comments/_comment.html.erbを作成。 render @article.commentの呼び出しによって。_commet.html.erbが自動で呼び出される。comment_counterはコレクションテンプレートの場合暗黙的に作られる。 #pre{{ <%= comment_counter + 1 %>. <%= comment.text %> }} -結局これによってコメントの番号とコメントのテキストが一覧表示されることになる。 *エンジンの作り込み[#l98d448e] **mountable型の作成 [#h0335811] ***プロジェクトの作成 [#k3f40a79] -以下のコマンドを実行 bundle exec rails plugin new ~/work/blorgh --mountable --skip-bundle -blorgh.gemspecを編集してTODOを削除したあと、bundle installの実行 bundle install --path=vendor/bundle ***Articleリソースの作成 [#d01f6ff6] -以下のコマンドを実行 bundle exec bin/rails generate scaffold article title:string text:text -モデルやコントローラーにネームスペースが付与されていることに注意。 #pre{{ $ bundle exec bin/rails generate scaffold article title:string text:text invoke active_record create db/migrate/20161025072006_create_blorgh_articles.rb create app/models/blorgh/article.rb invoke test_unit create test/models/blorgh/article_test.rb create test/fixtures/blorgh/articles.yml invoke resource_route route resources :articles invoke scaffold_controller create app/controllers/blorgh/articles_controller.rb invoke erb create app/views/blorgh/articles create app/views/blorgh/articles/index.html.erb create app/views/blorgh/articles/edit.html.erb create app/views/blorgh/articles/show.html.erb create app/views/blorgh/articles/new.html.erb create app/views/blorgh/articles/_form.html.erb invoke test_unit create test/controllers/blorgh/articles_controller_test.rb invoke helper create app/helpers/blorgh/articles_helper.rb invoke test_unit invoke assets invoke js create app/assets/javascripts/blorgh/articles.js invoke css create app/assets/stylesheets/blorgh/articles.css invoke css create app/assets/stylesheets/scaffold.css }} -miration実行 bundle exec rake db:migrate -test/dummyディレクトリで実行。http://localhost:3000/blorgh/articlesでアクセスできる。 bundle exec rails s ***Commentリソースの生成 [#t6b759ed] -記事1に対して複数のコメントがつけられる。 -以下のコマンドを実行。 #pre{{ $ bundle exec bin/rails generate model Comment article_id:integer text:text invoke active_record create db/migrate/20161025073042_create_blorgh_comments.rb create app/models/blorgh/comment.rb invoke test_unit create test/models/blorgh/comment_test.rb create test/fixtures/blorgh/comments.yml }} -migration実行 bundle exec rake db:migrate -以下の概略。app/views/blorgh/articles/show.html.erbにコメント一覧とコメント追加用のフォームを追加する。 *エンジンを使用する [#h6c29c33] **エンジンを使用するプロジェクトの作成 [#z12ddf05] -以下のコマンドで作成 bundle exec rails new ~/work/unicorn --skip-bundle -Gemfileを編集 #pre{{ gem 'devise' gem 'blorgh', path: "/Users/sora/work/blorgh" }} -bundle installを実行 bundle install --path=vendor/bundle -config/routes.rbを編集。 mount Blorgh::Engine, at: "/blog" **エンジンの設定 [#v675394d] -マイグレーションファイルのインストール #pre{{ $ bundle exec rake railties:install:migrations Copied migration 20170313063027_create_blorgh_articles.blorgh.rb from blorgh Copied migration 20170313063028_create_blorgh_comments.blorgh.rb from blorgh }} -マイグレーション実行。 bundle exec rake db:migrate -実行。http://localhost:3000/blog にアクセスする。 bundle exec rails s **アプリケーションが提供するクラスを使用する [#sdfec224] -アプリケーション側のクラスをエンジンから使いたい場合がある。例えばUser。ただしクラス名は固定したくない。 -とりあえずアプリケーション側でモデルクラスを作成。 #pre{{ bundle exec rails g model user name:string bundle exec rake db:migrate }} -エンジン側ではauther_nameテキストフィールドを利用して、ユーザー名として保存する。エンジンのapp/views/blorgh/articles/_form.html.erbのタイトルフィールドの下に以下を追加。 #pre{{ <div class="field"> <%= f.label :author_name %><br> <%= f.text_field :author_name %> </div> }} -※そのままだとauthor_nameの初期値が表示されないので以下のようにしないとだめかも? #pre{{ <%= f.text_field :author_name, :value => @article && @article.author ? @article.author.name: '' %> }} -エンジンのarticles_controller.rbを修正。 #pre{{ def article_params params.require(:article).permit(:title, :text, :author_name) end }} -app/models/blorgh/article.rbにしかるべく修正を行う。 #pre{{ attr_accessor :author_name belongs_to :author, class_name: "User" before_validation :set_author private def set_author self.author = User.find_or_create_by(name: author_name) end }} -関連付けに必要になるauther_idをblorgh_articlesテーブルに追加。エンジンのディレクトリで実行 #pre{{ $ bundle exec rails g migration add_author_id_to_blorgh_articles author_id:integer # 以下は必要ない? # bundle exec rails db:migrate }} -今度はアプリ側でマイグレーション実行。 #pre{{ $ bundle exec rake blorgh:install:migrations $ bundle exec rake db:migrate }} -作者名を記事のページに表示する。エンジンのapp/views/blorgh/articles/show.html.erbのTitleの上に追加。 #pre{{ <p> <b>Author:</b> <%= @article.author %> </p> }} -アプリケーション側でUserクラスにto_sを追加。 #pre{{ def to_s name end }} *エンジンを設定する [#tdc9ee93] -エンジンのlib/blorgh.rbに以下のメソッドを追加(mattr_accessorはrails独自の拡張でモジュールにセッターとゲッターを追加する). mattr_accessor :author_class -app/models/blorgh/article.rbのbelongs_toを変更 #pre{{ belongs_to :author, class_name: Blorgh.author_class self.author = Blorgh.author_class.constantize.find_or_create_by(name: author_name) }} -constantizeが呼び出されるようにしてもよい。 #pre{{ def self.author_class @@author_class.constantize end self.author = Blorgh.author_class.find_or_create_by(name: author_name) }} この場合belongs_toは次のようになる #pre{{ belongs_to :author, class_name: Blorgh.author_class.to_s }} -アプリケーション側ではconfig/initializers/blorgh.rbにイニシャライザを作成。クラス自身を渡すとテーブル参照が発生するのでクラスは文字列で渡す。 Blorgh.author_class = "User" *エンジンをテストする [#q5ed1b9d] -test/dummyディレクトリの下にダミーアプリケーションが作成される。 -このアプリを使ってテストする。 -その他testディレクトリで通常のRailsのテスト同様単体テスト、機能テスト、結合テストを実施することができる。 **機能テスト [#l8721296] -機能テストはエンジンではなく、test/dummyアプリで実行される(?)。これはコントローラーをテストするにはエンジンをホストするアプリケーションが必要なため。 -例えば以下のようにすることで、エンジンテストが可能? #pre{{ get :index, use_route: :blorgh setup do @routes = Engine.routes end }} *エンジンの機能を改良する [#q4b84168] -エンジンのモデルクラス、コントローラークラスは通常のオープンクラスとしてアプリ側から自由に拡張できる。 -エンジン側を以下のようにする。 #pre{{ # lib/blorgh/engine.rb module Blorgh class Engine < ::Rails::Engine isolate_namespace Blorgh config.to_prepare do Dir.glob(Rails.root + "app/decorators/**/*_decorator*.rb").each do |c| require_dependency(c) end end end end }} -decoratorではinstance_evalを使う。そのほかActiveSupport::Concernを使う手もあり。 -ビューは、最初にapp/views/blorgh/articles/index.html.erbが探されてそのあとエンジン側が検索される。そのためアプリ側で自由にカスタマイズできる。 -ルーティングはレンダリング対象によって変わる場合があるので、以下のように明示することもできる。 #pre{{ <%= link_to "Blog articles", blorgh.articles_path %> <%= link_to "Home", main_app.root_path %> }} -アセットも名前空間で分離されている。