自分はこんな感じでRailsアプリを作っております


Railsを使い始めてX年経ったこともあり、自分なりの開発パターンが形成されていることに気づきました。今日はそれを恥ずかしげもなく晒してみようかと思います。

Gemfile

自分の中で必須のgemたちです。その他はプロジェクトに合わせ適宜追加する感じです。

📄Gemfile
source 'https://rubygems.org'
ruby "2.0.0"

gem 'rails', '~> 4.0'
gem 'sqlite3'
gem 'sass-rails'
gem 'uglifier'
gem 'coffee-rails'
gem 'jquery-rails'
gem 'turbolinks'
gem 'jbuilder'

gem 'draper'
gem 'ransack'

group :development do
  gem 'rack-mini-profiler'
end

group :development, :test do
  gem 'pry-rails'
  gem 'pry-doc'
  gem 'pry-byebug'
  gem 'rspec-rails'
end

group :test do
  gem "shoulda-matchers"
  gem 'factory_girl_rails'
  gem 'capybara'
  gem 'poltergeist'
  gem 'launchy'
end

group :doc do
  gem 'sdoc', require: false
end

パフォーマンスは絶えず確認する

rack-mini-profiler というgemを使用して、常にパフォーマンスを確認しながら開発してます。SQLの発行数も確認できるのですごく便利。詳しくは過去の記事で説明してます。

rack-mini-profiler

検索はransackで

単純な検索機能であれば、ransack を導入することで大した手間なく実現できてしまいます。

📄blogs_controller.rb
class BlogsController < ApplicationController
...
  # GET /blogs
  # GET /blogs.json
  def index
    @q = Blog.search(params[:q])
    @blogs = @q.result(distinct: true)
  end
...
📄blogs/index.html.erb
<h1>Listing blogs</h1>

<%= search_form_for @q do |f| %>
  <%= f.text_field :title_cont, placeholder: "Title" %>
  <%= f.select :author_eq, Blog.pluck(:author), include_blank: true %>
  <%= f.text_field :description_cont, placeholder: "Description" %>
  <%= f.submit %>
<% end %>
...

ransack

たったこれだけで、検索機能が完成しました。

ViewModel層を作る

draper を導入すれば、簡単にViewModel層を追加することができます。オブジェクト指向的に実装できますし、ビューやヘルパーがごちゃごちゃしなくて済みます。詳細はRailsCastが分かりやすいと思います。

RailsCasts – #286 Draper

📄blogs_controller.rb
class BlogsController < ApplicationController
...
  def index
    @q = Blog.search(params[:q])
    @blogs = @q.result(distinct: true).decorate # BlogDecoratorを返す
  end
...
📄blog_decorator.rb
class BlogDecorator < Draper::Decorator
  delegate_all

  def short_description
    h.truncate(model.description, length: 10)
  end
end
📄index.html.erb
...
    <% @blogs.each do |blog| %>
      <tr>
        <td><%= blog.title %></td>
        <td><%= blog.author %></td>
        <td><%= blog.short_description %></td>
        <td><%= link_to 'Show', blog %></td>
        <td><%= link_to 'Edit', edit_blog_path(blog) %></td>
        <td><%= link_to 'Destroy', blog, method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
...

decorator

(サンプルがViewModel層として、うってつけではないですね。すいません・・・)

複数モデルを更新する場合はFacadeで

単純なマスタメンテナンス機能なんかは、scaffoldと同等のコードで構わないと思います。しかし、アプリがある程度の規模になってくると、一連のトランザクションで複数モデルを更新する場合が出てくると思います。そんなときは、facadeクラスを用意します。

📄blog_create_facade.rb
class BlogCreateFacade

  def initialize(blog)
    @blog = blog
  end

  def save
    return false unless @blog.valid?
    @blog.transaction do
      @blog.save!
      ping_to_notice @blog
      send_new_entry @blog
      # その他いろいろな処理
      # ...
      # ...
    end
    true
  rescue => e
    raise e
  end
end

Facadeを呼び出すコントローラ側は、すごくスッキリします。

📄blogs_controller.rb
...
  def create
    @blog = Blog.new(blog_params)
    facade = BlogCreateFacade.new(@blog)

    if facade.save
      redirect_to @blog, notice: 'Blog was successfully created.'
    else
      render action: 'new'
    end
  end
...

J2EEでいうところのServicesですかね。

テストは rspec + shoulda-matchers で

shoulda-matchers を使用すれば、いくらかのspecが簡単に書けます。一番役に立つのはバリデーションのspecかな。

例えば以下のようなモデルに対して・・・

📄blog.rb
class Blog < ActiveRecord::Base
  validates :title, presence: true
  validates :description, length: { maximum: 200 }
end

簡単にバリデーションのspecが書けちゃいます。

📄blog_spec.rb
require 'spec_helper'

describe Blog do
  it { should validate_presence_of(:title) }
  it { should ensure_length_of(:description).is_at_most(200) }
end

単純なバリデーションにまで、いちいちテストを書くのかどうかは意見が分かれると思います。しかし、shoulda-matchersを使えば1行でサクッと書けるので、私はテストするようにしてます。

shoulda-matchers

ヘルパーのテストは書かない

特定オブジェクトに属する複雑な表示処理は、前述のdraper にてViewModel層に定義し、テストを書いています。よって、ヘルパーには大したコードが残らない & どうせ目で見て確認しないとダメ なので、ヘルパーのspecは書いてません。

ビューのテストも書かない

昔書いてましたが、あまりメリットが無かったのでもう書いてません。私の場合、画面の開発って、あーでもない、こーでもないって考えながら実装していくパターンが多いので、どうしてもテスト・ファーストできません。テスト・アフターで「ブログ一覧と表示されているか?」ってテストするのもアレですし、そもそも体裁を確認するために結局目視するので、書くのをやめてしまいました。

Javascriptのテストも書かない・・・

フロントエンドをJavascriptでゴリゴリするようなナウいアプリなら、当然テストを書くべきでしょう。しかし私は、今のところその手のアプリを作っていません(恥)。また、jQueryでidやクラス指定してゴソゴソするような処理は、実画面のDOM構造と密接に関連して『しまう』ので、テストビューなどを用意した自動テストは上手くハマらないと思います。キモとなる重要なJavascript処理がある場合、poltergeist でテストするようにしてます。

書くのは models, decorators, facades, controllers, requests sepc

これだけ書いてれば98点ぐらい取れると思います。request specではcapybara とpoltergeist で受け入れテストしてます。Cucumberは、フィーチャーファイルを一緒に見てくれるユーザーがいないのでw使ってません。

フィクスチャではなく、factory_girl

ちょっとした規模のアプリになると、フィクスチャは破綻します。最初からfactory_girlを使用するようにしています。

📄factories.rb
FactoryGirl.define do

  factory :blog do
    title "test blog"
    author "Yamada Taro"
    description "this is a test blog.\n2nd line."
  end

  factory :masuda_blog, class: Blog do
    title "匿名ダイアリー"
    author nil
    description "this is a masuda blog."
  end
end
📄blog_spec.rb
require 'spec_helper'

describe Blog do
...
  describe "masuda?" do
    subject { blog.masuda? }
    let(:blog) { FactoryGirl.create(:masuda_blog) }
    it { should be_true }
  end
end

 

以上

 

関連する記事