オレオレscaffold generator を作る

Ruby on Rails の Scaffold generator はアプリの元になるコードを生成するに便利ですが実際のアプリを作る際には、XMLをレスポンスするコードは要らないとか、日本語じゃないとか、テンプレートがイマイチだとかいろいろ不満点があると思います。 標準のScaffoldの作成するコードを少し変えるだけならとても簡単です。


オレオレscaffold generatorを置く場所

まず、オレオレscaffold generator を置く場所ですが、Ruby on Rails guidesの 2.3 The Rails Generation: Generatorsにあるように RAILS_ROOT/lib/generators 、 ~/.rails/generators などが考えられます。プロジェクト固有のオレオレscaffoldなら RAILS_ROOT/lib/generatorsが良いかもしれませんが、今回は オレオレ ということで ~/.rails/generators に作ってみます。

標準のScaffoldをコピー

Scaffold generatorはgeneratorのコードと生成するコードのテンプレートから出来ています。今回はgeneratorはScaffoldをそのまま使うので、 ScaffoldGeneratorクラスを継承するだけです。テンプレートは Scaffoldのものをコピーし自分用に変更します。

% mkdir  ~/.rails/generators/my_scaffold
% cd ~/.rails/generators/my_scaffold
% cp -r /opt/local/lib/ruby/gems/1.8/gems/rails-2.3.2/lib/rails_generator/generators/components/scaffold/templates .
#           ↑ gem のパスは自分の環境に合わせて変えて下さい
% cat > my_scaffold_generator.rb
class MyScaffoldGenerator < ScaffoldGenerator
end
^D
%
% vi templates/controller.rb
% vi templates/view_index.html.erb
....
%

使ってみる

% rails test
% cd test
% ./script/generate --help 
Usage: script/generate generator [options] [args]
Rails Info:
    -v, --version                    Show the Rails version number and quit.

  ...

Installed Generators
  User: my_scaffold           ← オレオレscaffold generatorがあります !!
  Rubygems: gruff, i18n, i18n_locale, i18n_scaffold, i18n_translation, rspec, rspec_controller, rspec_model, rspec_scaffold, web_service
  Builtin: controller, helper, integration_test, mailer, metal, migration, model, observer, performance_test, plugin, resource, scaffold, session_migration
   ....

% ./script/generate my_scaffold todo due:date task:string   ← 使って試してみる


とても簡単です (^_^)

私の作った オレオレテンプレート

  • controller.rb
# <%= class_name %>コントロラー
class <%= controller_class_name %>Controller < ApplicationController
  # 一覧
  def index
    @<%= table_name %> = <%= class_name %>.all
  end

  # 詳細表示
  def show
    @<%= file_name %> = <%= class_name %>.find(params[:id])
  end

  # 新規作成画面
  def new
    @<%= file_name %> = <%= class_name %>.new
  end

  # 編集画面表示
  def edit
    @<%= file_name %> = <%= class_name %>.find(params[:id])
  end

  # 新規作成処理
  def create
    @<%= file_name %> = <%= class_name %>.new(params[:<%= file_name %>])

    if @<%= file_name %>.save
      flash[:notice] = '<%= class_name %> を作成しました。'
      redirect_to(@<%= file_name %>)
    else
      render :action => "new"
    end
  end

  # 編集処理
  def update
    @<%= file_name %> = <%= class_name %>.find(params[:id])

    if @<%= file_name %>.update_attributes(params[:<%= file_name %>])
      flash[:notice] = '<%= class_name %> を更新しました。'
      redirect_to(@<%= file_name %>)
    else
      render :action => "edit"
    end
  end

  # 削除
  def destroy
    @<%= file_name %> = <%= class_name %>.find(params[:id])
    @<%= file_name %>.destroy

    redirect_to(<%= table_name %>_url)
  end
end
  • view_index.html.erb
<h1><%= plural_name %>一覧</h1>

<table>
  <tr>
<% for attribute in attributes -%>
    <th></th>
    <th><%= attribute.column.human_name %></th>
<% end -%>
  </tr>

<%% @<%= plural_name %>.each do |<%= singular_name %>| %>
  <tr>
    <td>
      <%%= link_to '表示', <%= singular_name %> %>
      <%%= link_to '編集', edit_<%= singular_name %>_path(<%= singular_name %>) %>
      <%%= link_to '削除', <%= singular_name %>, :confirm => '削除してよいですか?', :method => :delete %>
    </td>
<% for attribute in attributes -%>
    <td><%%=h <%= singular_name %>.<%= attribute.name %> %></td>
<% end -%>
  </tr>
<%% end %>
</table>

<br />

<%%= link_to '新規作成', new_<%= singular_name %>_path %>
  • view_show.html.erb
<h1><%= singular_name %> 詳細</h1>

<div class="detail">
  <dl>
<% for attribute in attributes -%>
    <dt><%= attribute.column.human_name %>:</dt>
    <dd><%%=h @<%= singular_name %>.<%= attribute.name %> %></dd>
<% end -%>
  </dl>
</div>

<%%= link_to '戻る', <%= plural_name %>_path %>
  • view_edit.html.erb (view_new.html.erbもほぼ同じ)
<h1><%= singular_name %> 編集</h1>

<div class="entry">
<%% form_for(@<%= singular_name %>) do |f| %>
  <%%= f.error_messages %>

  <dl>
<% for attribute in attributes -%>
    <dt><%%= f.label :<%= attribute.name %> %></dt>
    <dd><%%= f.<%= attribute.field_type %> :<%= attribute.name %> %></dd>
<% end -%>
  </dl>
  <div class="op_buttons">
    <%%= f.submit '更新' %>
  </div>
<%% end %>
</div>

<%%= link_to '戻る', <%= plural_name %>_path %>
  • style.css の追加分
div.detail, div.entry {
}

div.detail dt, div.entry dt {
    float: left;
    width: 5em;
    padding: 2px 0px 2px 10px;
}

div.detail dd, div.entry dd {
    margin-left: 5em;
    padding: 2px 0px 2px 20px;
}