Railsアプリの設計をMVCごとに見直しリファクタリングして連載総まとめ:開発現場でちゃんと使えるRails 4入門(12)(2/2 ページ)
エンタープライズ領域での採用も増えてきたRuby on Railsを使ってWebアプリケーションを作るための入門連載。最新版の4に対応しています。今回は、サンプルプロジェクトをMVCごとにRailsアプリの設計を見直してリファクタリングすることで、これまでの連載のおさらいをします。
モデルの見直し
次に連載第5回の「ActiveRecordの基本機能とマイグレーション、バリデーション」で解説したMVCの「M」、モデルについて見直してみましょう。モデルは、その設計によって他のコンポーネントがシンプルに作れるようになるなど、重要な役割を担っています。モデルの設計を見直すことで、アプリケーションを洗練させることができます。
コントローラーのロジックの吸い取り
先ほど「BooksController」の「index」アクションを見ましたが、以下のように「@books」の周辺があまりキレイではありませんでした。
def index @query = Query.new(params[:query]) if @query.valid? @books = Book.where("title like :keyword OR author like :keyword", keyword: @query.keyword) else @books = Book.all end end
「@query」によって返すべき「Book」が異なるなら、その判定は「Book」が担うべきです。そこで「Book(app/models/book.rb)」に次の「search」スコープを定義します。
scope :search, ->(query=nil) { if query && query.valid? where("title like :keyword OR author like :keyword", keyword: query.keyword) else all end }
これにより「books#index」は次のように変更できます。このアクションのスペックを実行してもエラーは発生しておらず、コントローラーからモデルにロジックが移行していることが分かります。
def index @query = Query.new(params[:query]) @books = Book.search(@query) end
モジュールの活用
複数のモデルで同じような関心事(concern)を抱えていることがあります。
例えばPV数を保存する属性を持つ複数のモデルでは、いずれのモデルでもランキングを出力したいことがあります。また、画像のパスを保存する属性を持つ複数のモデルでは、ファイルサイズのバリデーションを実装したいことがあります。
このように複数のモデルを横断する関心事は「concerns」モジュールによりまとめ上げるといいでしょう。ここではランキングを出力するスコープを定義するモジュールの例を示します。
module Ranking # 必要なカラム:'monthly_pv_count'、'weekly_pv_count'、'daily_pv_count' extend ActiveSupport::Concern included do scope(:ranking), ->(term) { avaival_term = %w(monthly weekly daily) if avaival_term.include?(term) order("#{term}_pv_count desc") else raise "不正な値です。'monthly'、'weekly'、'daily'のいずれかを渡してください。" end } end end
このモジュールを「app/models/concerns/ranking.rb」として保存します。「ActiveSupport::Concern」を「extend」したモジュールでは「included」のブロック中でスコープなどを定義できます。このモジュールを以下のようにモデルで読み込むことで「ranking」スコープがそれぞれで定義されます。
class Book < ActiveRecord::Base include Ranking end class Review < ActiveRecord::Base include Ranking end
ビューの見直し
最後に連載第8回の「RailsのテンプレートエンジンSlimの書き方とActionViewのヘルパーメソッド、レイアウトの使い方」で紹介したMVCの「V」、ビューです。ビューの見直しは、部分テンプレートやヘルパーにより共通部分を切り出すことで行います。
また「yield」と「content_for」によりコンテンツを埋め込む方法もあります。それぞれ「Admin::BooksController」のビューを元に説明していきます。
部分テンプレートのおさらい
部分テンプレートはビュー中の「render」メソッドで呼び出し、「_form.html.slim」のようにファイル名の先頭を「_」とするファイルに定義します。
/ app/views/admin/books/new.html.slim = render partial: "form", locals: {book: @book, url: admin_books_path} / app/views/admin/books/_form.html.slim = form_for book, url: url do |f| .field = f.label :title br = f.text_field :title ……
また、コレクションに対しては部分テンプレート名とローカル変数名を一致させることで次のように簡単に呼び出せます。以下の実装は全て同じ内容になります。
/ よくあるレンダリング - @books.each do |book| = render partial: 'book', locals: {book: book} / このようにもできる - @books.each do |book| = render book / このようにも = render partial: 'book', collection: @books
ヘルパーの活用
ヘルパーには、標準で用意されているヘルパー、「app/helpers」でモジュールに定義するヘルパー、コントローラーで定義したメソッドをヘルパーメソッドとして使えるように宣言したヘルパーの3種類があります。
開発者が自ら定義するヘルパーはビューだけで使う場合は「app/helpers」で、コントローラーでも使う場合はコントローラーで定義するといいでしょう。
「yield」の活用
これまで「yield」はレイアウトで一度呼び出すだけでした。しかし、実は「yield」は複数設定できます。
以下のレイアウトではシンボルを渡した「yield」があり、そこに埋め込まれるコンテンツの有無を判定する「content_for?」メソッドによってレイアウトが切り替わります。
doctype html html lang="ja" head title BookLibrary = stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true = javascript_include_tag "application", "data-turbolinks-track" => true = csrf_meta_tags - if content_for?(:side) .side = yield(:side) .main = yield - else .full = yield
「yield(:side)」に埋め込まれるコンテンツがないとき、このレイアウトは従来と同じように挙動します。そして「yield(:side)」に埋め込むコンテンツはビューの中で次のように定義します。
= content_for(:side) do h2 サイドカラム h2 メインカラム
このように「content_for」の引数で指定した「yield」に埋め込むコンテンツをブロック中で定義します。サイドバーやフッターなどで使うことが想定されていますが、実際のところは都度定義する必要があるのでレイアウト中で部分テンプレートを呼ぶ方がよく見られる気がします。
1日あれば、あのイカしたサービスのあのイカした機能を再現できる
他にも見直す箇所はいくらでもありますが、MVCのコンポーネントをそれぞれ説明できたので本連載のおさらいはここまでとします。残りの改善箇所については「book_library」の「11」ディレクトリを参照してください。
また今回をもちまして筆者(林慶)による予定されている連載は終了となります。これまで支えてくださった関係者の方、そして読者の方に感謝の気持ちを表したいと思います。
Railsの最初のハードルは「rails g scaffold article title:string body:text」でブログを作ることです。そこからのハードルは1日かければ乗り越えられるものがほとんどでした。だから「1日あれば、あのイカしたサービスのあのイカした機能を再現できるんだ、どんなもんだい」ってな気分になれるのがRails開発の楽しいところだと私は思っています。
皆さんも、ぜひ楽しいRails開発を見つけてください。それでは。
- Railsアプリの設計をMVCごとに見直しリファクタリングして連載総まとめ
- 「設定より規約」のRailsで必要なセッティングの基礎知識と国際化/多言語対応
- ActionMailerのSMTP設定、テンプレートで送信&ActiveModelの基本的な使い方とバリデーション
- RailsテストフレームワークRSpecのインストールと基本的な使い方、基礎文法
- RailsのテンプレートエンジンSlimの書き方とActionViewのヘルパーメソッド、レイアウトの使い方
- Rails開発を面白くするアクションコントローラーの5大機能とルーティングの基本
- ActiveRecordにおけるモデルの「関連」とコールバックの使い方
- ActiveRecordの基本機能とマイグレーション、バリデーション
- 現場で使えるか見極めたいRails 4.1の新機能8選
- 特定データに関するscaffoldアクションの実装&基礎的なリファクタリング手法
- scaffoldの中身を理解するためにMVCコンポーネントと7つのアクションを個別で自作する
- 簡単インストールから始める初心者のためのRuby on Railsチュートリアル
著者プロフィール
林 慶(Rails技術者認定シルバー試験問題作成者)
平成2年大阪生まれ。2006年から高専で情報工学を学んでいたが当時は所謂プログラミングができない工学生だった。卒業後、高専の専攻科に上がったもののマンネリ化したキャンパスライフに飽きたため休学して渡豪。そこでプログラミングに対するコンプレックスを克服するためにRuby on Railsなどでアプリケーションを作ることを覚える。
帰国後から現在までは復学し推薦システムに関する研究を行いながら、アジャイルウェアでRuby on Railsアプリケーションの開発業務に従事している。
好きなメソッドはinject。
監修者プロフィール
山根 剛司(Ruby業務開発歴7年)
兵庫県生まれ。1997年からベンチャー系のパッケージベンダーで10年間勤務。当時、使用していた言語はJavaとサーバーサイドJavaScript。
2007年よりITコンサル会社に転職し、Rubyと出会って衝撃を受ける。基幹システムをRuby on Railsで置き換えるプロジェクトに従事。それ以来Ruby一筋で、Ruby on Railsのプラグインやgemも開発。
2013年より、株式会社アジャイルウェアに所属。アジャイルな手法で、Ruby on Railsを使って企業向けシステムを構築する業務に従事。
Ruby関西所属。好きなメソッドはtap。
Twitter:@spring_kuma、Facebook:山根 剛司
Copyright © ITmedia, Inc. All Rights Reserved.
関連記事
Ruby on Rails 4.1でDevOps環境を考慮した体系的な知識を付ける
エンタープライズ領域での高速なWebアプリケーション開発にも採用され始めているRuby on Rails。最新版である4.1の実践的な解説書が登場した。DevOps環境を前提としたチュートリアルが掲載されているなど、現代的な内容になっている。- @IT自分戦略研究所 資格辞典:Rails技術者認定試験(Rails 3 Certified Programmer)
- 資格試験は転職に役立つか:Rails技術認定試験がスタート、合格者に聞いた