はじめに

今までRailsのscaffoldは、素早くアプリを作るためのツールだと思っていました。しかし、scaffoldはRailsの規約が適用されているバイブル的な存在であり、scaffoldに従って書くことで効率的にサイトを作っていくことができる!と教わったので、生成されるコードで気になる所を調べてみました。その時のメモ。

※このメモを書いたのは2013年7月で書いた時点ではRails触って3ヶ月位だったので情報が間違っているor古くなっている可能性があります。

scaffold差分

生成されるコード 実行コード

$ rails generate scaffold Item name:string price:integer description:text

migrateファイル

class CreateItems < ActiveRecord::Migration
  def change
    create_table :items do |t|
      t.string :name
      t.integer :price
      t.text :description

      t.timestamps
    end
  end
end
  • ActiveRecordのclassを継承していますが、これはSQLでリレーショナルデータベースを扱う際に必要なもの

Ruby on Rails3で学ぶWeb開発のキホン(3):「ActiveRecord」の基本とデータの参照 (1/2) - @IT

  • NOSQLでは使われないみたい

MongoDB を Rails から使う、導入編 - happy lie, happy life

これで無事に、migrateができました。失敗したorやり直したい時には

$ rake db:migrate:redo [STEP=ステップ数]

で戻すことができます。

config/routes.rb

Sample::Application.routes.draw do
  resources :items
end
  • resources :itemsがscaffoldで生成されています。これによって、routingが追加されてきます。ちなみに、 resource :itemとすれば、単体で扱う場合に重宝するみたいです。

Ruby on Rails ルーティング - ayaketanのプログラミング勉強日記

  • 設定されたroutingは rake routes コマンドで確認ができます。
   Prefix Verb   URI Pattern               Controller#Action
    items GET    /items(.:format)          items#index
          POST   /items(.:format)          items#create
 new_item GET    /items/new(.:format)      items#new
edit_item GET    /items/:id/edit(.:format) items#edit
     item GET    /items/:id(.:format)      items#show
          PATCH  /items/:id(.:format)      items#update
          PUT    /items/:id(.:format)      items#update
          DELETE /items/:id(.:format)      items#destroy
  • Rails3とは違ってPATCHが追加されています。

次期RailsがPATCHメソッドを採用 - ぶろぐ。@はてな

app/controller/items_controller.rb

class ItemsController < ApplicationController
  before_action :set_item, only: [:show, :edit, :update, :destroy]

  def index
    @items = Item.all
  end

  def show; end

  def new
    @item = Item.new
  end

  def edit; end

  def create
    @item = Item.new(item_params)

    respond_to do |format|
      if @item.save
        format.html { redirect_to @item, notice: 'Item was successfully created.' }
        format.json { render action: 'show', status: :created, location: @item }
      else
        format.html { render action: 'new' }
        format.json { render json: @item.errors, status: :unprocessable_entity }
      end
    end
  end

  def update
    respond_to do |format|
      if @item.update(item_params)
        format.html { redirect_to @item, notice: 'Item was successfully updated.' }
        format.json { head :no_content }
      else
        format.html { render action: 'edit' }
        format.json { render json: @item.errors, status: :unprocessable_entity }
      end
    end
  end

  def destroy
    @item.destroy
    respond_to do |format|
      format.html { redirect_to items_url }
      format.json { head :no_content }
    end
  end

  private

    def set_item
      @item = Item.find(params[:id])
    end

    def item_params
      params.require(:item).permit(:name, :price, :description)
    end
end
  • before_actionを使うことで、 @item = Item.find(params[:id]) を実行しているので、showやeditメソッドがスッキリしてますね。

  • action以外のメソッドを追加してしまうと、意図しないコードが直接実行されてしまう可能性があるので、privateメソッドにする必要があるみたいです。

  • Rails3でのattr_accessibleはstrong parametersに変更。

Rails 4でmass assignmentを防止する方法: strong parameters - memo.yomukaku.net

  • renderとredirect_toの違いですが、renderはそのままデータをviewテンプレートに貼り付けるイメージで、redirectはもう一度URLにリクエストして表示しています。もし、createメソッドのredirect_toをrenderに変更してしまうと、動作はしますが、クライアント側でブラウザバック等を使って何回も容易にに新しいデータをcreateできてしまう危険性が生まれてしまいます。

ruby on rails - Are redirect_to and render exchangeable? - Stack Overflow (英語ですが一番図が分かりやすかったです。redirectすると間にGETリクエストが入るのが大事みたい。)

##app/views/items/index.html.erb

<h1>Listing items</h1>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Price</th>
      <th>Description</th>
      <th></th>
      <th></th>
      <th></th>
    </tr>
  </thead>

  <tbody>
    <% @items.each do |item| %>
      <tr>
        <td><%= item.name %></td>
        <td><%= item.price %></td>
        <td><%= item.description %></td>
        <td><%= link_to 'Show', item %></td>
        <td><%= link_to 'Edit', edit_item_path(item) %></td>
        <td><%= link_to 'Destroy', item, method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<%= link_to 'New Item', new_item_path %>
  • ほとんどは、htmlが理解できていれば大丈夫そうですが、注目するのはedit_item_path(item)の部分ですね。

controllerのところではredirect_to items_urlが使われていて、XXX_path と XXX_urlで何が違うのか知らなかった。調べてみると相対パスと絶対パスの違いがあるみたい。(link_toなら出力する量が少ないXXX_pathの方がいいらしい) Railsの_pathと_urlに関するメモ - $ cat /var/log/shin

app/views/items/index.json.jbuilder

rails4からJSON出力にjbuilderが使われているみたいです。

json.array!(@items) do |item|
  json.extract! item, :name, :price, :description
  json.url item_url(item, format: :json)
end
  • これで、JSON形式の出力もview側に書けばいいのでcontrollerがスッキリするみたいですね。

rails4のjbuilderを使ってみた - ガイアが俺にもっとコード書けと囁いている

app/views/items/_form.html.erb

<%= form_for(@item) do |f| %>
  <% if @item.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@item.errors.count, "error") %> prohibited this item from being saved:</h2>

      <ul>
      <% @item.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :name %><br>
    <%= f.text_field :name %>
  </div>
  <div class="field">
    <%= f.label :price %><br>
    <%= f.number_field :price %>
  </div>
  <div class="field">
    <%= f.label :description %><br>
    <%= f.text_area :description %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>
  • 実際にどのように変換されるのかは次のサイトが分かりやすかった

「Ruby on Rails Tutorial」ビューのform_forと実際に生成されたHTMLフォーム Ruby on RailsでWebサイト公開!に挑戦中

おわりに

今回はこれで終了。

Railsの「設定よりも規約」という考えが反映され、普段気にせず使っているコードも、理解を深めていけばいくほどプログラマーにとって便利な動きをしてくれていることがわかってきました。

今回scaffoldを追ってみたことで、以前よりはレールに乗れて脱線することも減るかなと思います。