Railsアプリの設計をMVCごとに見直しリファクタリングして連載総まとめ開発現場でちゃんと使えるRails 4入門(12)(1/2 ページ)

エンタープライズ領域での採用も増えてきたRuby on Railsを使ってWebアプリケーションを作るための入門連載。最新版の4に対応しています。今回は、サンプルプロジェクトをMVCごとにRailsアプリの設計を見直してリファクタリングすることで、これまでの連載のおさらいをします。

» 2015年01月08日 18時00分 公開
[著:林慶、監修:山根剛司株式会社アジャイルウェア]

※編集部注

本連載はRuby on Rails 4の入門連載です。Rubyについて学びたい方は連載「若手エンジニア/初心者のためのRuby 2.1入門」をご覧ください。

「開発現場でちゃんと使えるRails 4入門」のインデックス

連載目次

 前回の『「設定より規約」のRailsで必要なセッティングの基礎知識と国際化/多言語対応』まで、サンプルプロジェクトの「book_library」を題材にRailsのさまざまな機能を紹介してきましたが、今回はRailsアプリケーション開発を紹介してきた本連載のおさらいとして、サンプルプロジェクトをMVCごとにリファクタリングしたいと思います。

 「book_library」は社内の書籍を管理するためのアプリケーションで、これまでRailsの各機能を紹介するため場当たり的にさまざまな機能を盛り込んできましたが、もっとシンプルに作ってみましょう。

コントローラーの見直し

 まずは、連載第7回の「Rails開発を面白くするアクションコントローラーの5大機能とルーティングの基本」で解説したMVCの「C」、コントローラーです。コントローラーの見直しは名前空間やネストによる外部構造と、アクションの定義などの内部構造に分けて進めていきます。

外部構造の見直し

 コントローラーの外部構造は「ルーティング」により定義されますが、それに従って「app/controllers」以下のファイルの配置も決まります。現在の「config/routes.rb」は次のコードのようになっていますが、注意して変更していきます。

  1. BookLibrary::Application.routes.draw do
  2. root 'books#index'
  3. resources :books
  4. resources :users do
  5. get 'booking', on: :collection
  6. post 'message', on: :member
  7. resources :books, only: %i(index)
  8. end
  9. namespace :admin do
  10. resources :books, only: %i(index)
  11. end
  12. get 'admin' => 'admin/books#index'
  13. get 'admin/:id' => 'admin/books#show'
  14. post 'admin' => 'admin/books#create'
  15. end

 まず2行目の「root」の定義ですが、これはこのままでもよいでしょう。

 ですが、次の「resources :books」と「namespace :admin」のブロック内の「resources :books」はいずれも「books」のリソースを取り扱うルーティングです。しかし、本来であれば管理側でリソースの登録や更新、削除などをしたいところです。

 一方、4行目の「resouces :users」はユーザー自身にユーザー登録や更新する操作をさせたいので、このままとしておきます。しかし、このブロック内部の「get」や「post」は必要な処理かもしれませんが、「resources :books」は「index」アクションだけでそれほど重要ではないかもしれません。

 最後の「get」や「post」によるルーティングは連載中の説明のために追加しましたが、見るからに違和感がありますね。「namespace :admin」のルーティングで同じアクションを扱えるので削除してしまってもいいでしょう。

 以上の内容を踏まえてルーティングを再定義すると、次のようになりました。ちなみに「namespace :admin」のブロック中で「root」を定義すると、「/admin」のルーティングを設定できます。

  1. BookLibrary::Application.routes.draw do
  2. # ユーザーサイド
  3. root 'books#index'
  4. resources :books, only: %w(index show)
  5. resources :users do
  6. get 'booking', on: :collection
  7. post 'message', on: :member
  8. end
  9. # 管理者サイド
  10. namespace :admin do
  11. root 'books#index'
  12. resources :books
  13. end
  14. end

内部構造の見直し

 前節のルーティングの再定義により、コントローラーごとにアクションを見直す必要があります。まず「BooksController(app/controllers/books_controller.rb)」から取り掛かりましょう。

 このコントローラーは連載初期に「scaffold」により生成し、そのまま使ってきました。ですが、「new」「create」「edit」「update」「destroy」のアクションは「Admin::BooksController(app/controllers/admin/books_controller.rb)」に移動させたいと思います。対象の部分を切り取って、フィルターのオプションなどを整理すると「BooksController」は次のようになります。

  1. class BooksController < ApplicationController
  2. before_action :set_book, only: [:show]
  3. def index
  4. @query = Query.new(params[:query])
  5. if @query.valid?
  6. @books = Book.where("title like :keyword OR author like :keyword", keyword: @query.keyword)
  7. else
  8. @books = Book.all
  9. end
  10. end
  11. def show
  12. end
  13. private
  14. def set_book
  15. @book = Book.find(params[:id])
  16. end
  17. end

 切り取った部分は「Admin::BooksController」で再利用しますが、リダイレクト先やビューのリンクなどが「admin」名前空間の中でないため、適宜修正する必要があります。修正した内容については「book_library」の「11」ディレクトリのコードを参照してください。

 また、ビューに関しては削除したアクションへのリンクなど不必要なものを取り除きます。「index」アクションのビューを次に示します。

  1. h1 = t('.title')
  2. = form_for @query, url: books_path, method: :get do |f|
  3. = f.text_field :keyword
  4. = f.submit '検索'
  5. table
  6. thead
  7. tr
  8. th Title
  9. th Author
  10. th Outline
  11. tbody
  12. - @books.each do |book|
  13. tr
  14. td = link_to book.title, book
  15. td = book.author
  16. td = book.outline

スペック(spec)の追加

 ここまででも「books#index」のリファクタリングができましたが、次の変更に備えてスペック(spec)を追加しておくのも良い考えです。「spec/controllers/books_controller_spec.rb」に次のように「index」アクションなどのテストを追加します(テストについては連載代9回の「RailsテストフレームワークRSpecのインストールと基本的な使い方、基礎文法」を参照してください)。

  1. require 'rails_helper'
  2. RSpec.describe BooksController, :type => :controller do
  3. describe "index" do
  4. # @queryにQueryのインスタンスが代入される
  5. it "assigns a query as @query" do
  6. get :index, {}, {}
  7. expect(assigns(:query)).to an_instance_of Query
  8. end
  9. # クエリがないとき、@booksに全てのBookが代入される
  10. it "assigns all books as @books without query" do
  11. b1 = Book.create!(title: 'hoge')
  12. b2 = Book.create!(title: 'fuga')
  13. get :index, {}, {}
  14. expect(assigns(:books)).to eq [b1, b2]
  15. end
  16. # @booksにクエリで要求されたBookが代入される
  17. it "assigns books as @books required by query" do
  18. b1 = Book.create!(title: 'hoge')
  19. b2 = Book.create!(title: 'fuga')
  20. get :index, {query: {keyword: 'hoge'}}, {}
  21. expect(assigns(:books)).to eq [b1]
  22. end
  23. end
  24. ……
  25. end
       1|2 次のページへ

Copyright © ITmedia, Inc. All Rights Reserved.

スポンサーからのお知らせPR

Coding Edge 鬮ォ�ェ陋滂ソス�ス�コ闕オ譁溷クキ�ケ譎「�ス�ウ驛「�ァ�ス�ュ驛「譎「�ス�ウ驛「�ァ�ス�ー

髫エ蟷「�ス�ャ髫エ魃会スス�・髫エ蟶キ�」�ッ闖ォ�」

注目のテーマ

4AI by @IT - AIを作り、動かし、守り、生かす
Microsoft & Windows最前線2025
AI for エンジニアリング
ローコード/ノーコード セントラル by @IT - ITエンジニアがビジネスの中心で活躍する組織へ
Cloud Native Central by @IT - スケーラブルな能力を組織に
システム開発ノウハウ 【発注ナビ】PR
あなたにおすすめの記事PR

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。