実行環境:
ruby 1.9.3
Rails 3.1.3
ActiveRecord 3.1.3
複数の Scaffold を作っていて、それらの動作や見栄えを変更しようとすると View テンプレートをいちいちテーブルのカラムを1つ1つちくちく直したりというのが結構面倒です。ruby 1.9.3
Rails 3.1.3
ActiveRecord 3.1.3
1つアプリの中に多数のモデル(DBのテーブル)を作っている場合、管理画面は変更が面倒という理由でデフォルトの Scaffold のままだったりします。ちょっと思いたって少しいじろうかなと思っても、修正するファイルが多すぎてすぐにへこたれてしまいます。なにせ1つの Scaffold に対して Controller が1ファイル、Viewが5ファイルあるので、20の Scaffold があると120ファイルくらいあるわけですから。 (>_<)
どの Scaffold でも Controller 名と Model 名、カラム名が違うだけでファイルの構成もやってることもたいてい同じなので、Controller 名やら Model 名やらカラム名やらを動的に取得して共通化してやれば、たくさんのファイルを管理しなくてもよいんじゃないか?
そんなことを考えて Controller と View のファイルから個別のモデルに関する記述を取り除いて、どの Scaffold でも継承するだけで使えるようなものを作ってみたいと思います。
基本方針
Controller について
ApplicationController を継承した CommonScaffoldController(名前はなんでもいいですが)を作り、index や edit などのアクションメソッドを汎用性を持たせた形で書きこみます。各 Scaffold (以下では例として Users で説明します。)のコントローラは CommonScaffoldController を継承させるだけで中身を空っぽにします。アクションは全て CommonScaffoldController で定義するという作戦です。# ここできっちりと定義しておく end # アクションは全てこのファイルで定義します endclass CommonScaffoldController < ApplicationController def index
# 個別のコントローラは継承するだけ endclass UsersController < CommonScaffoldController
View について
デフォルトの View ファイルの構成をそのまま使います。ajax を使えばもうちょっときれいにまとめられると思いますが、今回の趣旨とは関係ないので今回はそのままです。app/views/ の直下に common_scaffold フォルダを作り、その中に index.html.erb やら _form.html.erb やらを入れます。
[common_scaffold]
├ index.html.erb
├ show.html.erb
├ new.html.erb
├ edit.html.erb
└ _form.html.erb
上記の CommonScafffoldController の中でこのディレクトリの中の View ファイル達を render に使います。つまりどの Scaffold でも同じ View ファイルが使われるようにします。
具体的なコード
CommonScaffoldController
各 Scaffold のコントローラがこの CommonScaffoldController を継承します。controller_name で個別のコントローラ名が取得でkるのでそれを元に対応するモデル名、モデルクラスを取得します。# @model_class, @model_instance_name を取得 def index @objects = @model_class.all end def show @object = @model_class.find(params[:id]) # @object = @model_class.where("? = ?", @model_class.primary_key, params[:id]).first end def new @object = @model_class.new end def edit @object = @model_class.find(params[:id]) end def create @object = @model_class.new(params[@model_instance_name]) respond_to do |format| if @object.save format.html { redirect_to @object, notice: "%s was successfully created."%(@model_instance_name) } else format.html { render 'new' } end end end def update @object = @model_class.find(params[:id]) respond_to do |format| if @object.save format.html { redirect_to @object, notice: "%s was successfully updated."%(@model_instance_name) } else format.html { render 'edit' } end end end def destroy @object = @model_class.find(params[:id]) @object.destroy respond_to do |format| format.html { redirect_to :action => :index } end end private def get_model_class @model_class = controller_name.classify.constantize # モデルクラス(Userなど)を取得 @model_instance_name = @model_class.model_name.underscore # モデルのインスタンス名を取得 end endclass CommonScaffoldController < ApplicationController before_filter get_model_class
common_scaffold/*.html.erb
Viewファイルの中身からも個別の Scaffold に関わる名前を駆逐します。index.html.erb
<h1>Listing <%= @model_instance_name.plurarize %></h1>
<table>
<tr>
<%- @model_class.columns.each do |column| -%>
<th><%= @model_class.human_attribute_name(column.name) %></th>
<%- end -%>
</tr>
<%- @objects.each do |object| -%>
<tr>
<%- @model_class.columns.each do |column| -%>
<td><%= @model_class.send(column.name) %></td>
<%- end -%>
<td><%= link_to 'Show', object %></td>
<td><%= link_to 'Edit', :controller => controller.controller_name, :action => :edit, :id => object.id %></td>
<td><%= link_to 'Destroy', object, confirm: 'Are you sure?', method: :delete %></td>
<td></td>
</tr>
<%- end -%>
</table>
<%= link_to "New %s"%(@model_instance_name), :controller => controller.controller_name, :action => :new %>
show.html.erb
<p id="notice"><%= notice %></p>
<%- @model_class.columns.each do |column| -%>
<p>
<b><%= column.name.humanize %></b>
<%= @object.name %>
</p>
<%- end -%>
<%= link_to 'Edit', :controller => controller.controller_name, :action => :edit, :id => @object.id %></td>
<%= link_to 'Back', :controller => controller.controller_name, :action => :index %></td>
new.html.erb
<h1>New <%= @model_instance_name %></h1>
<%= render 'form' %>
<%= link_to 'Back', :controller => controller.controller_name, :action => :index %>
edit.html.erb
<h1>Edit <%= @model_instance_name %></h1>
<%= render 'form' %>
<%= link_to 'Show', @object %>
<%= link_to 'Back', :controller => controller.controller_name, :action => :index %>
_form.html.erb
<%= form_for(@object) do |f| %>
<% if @object.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@object.errors.count, "error") %> prohibited this <%= @model_instance_name %> from being saved:</h2>
<ul>
<% @object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%- @model_class.columns.each do |column| -%>
<div class="field">
<%= f.label column.name %><br />
<%= f.text_field column.name %>
</div>
<%- end -%>
<div class="action">
<%= f.submit %>
</div>
<% end %>
個別の Scaffold の設定
Controller は CommonScaffoldController を継承するだけ、Model は標準の Scaffold で作られるもののまま、View ファイルは削除します。Controller
Controller は CommonScaffoldController を継承するだけで、メソッドの定義は一切不要。
Views
class UsersController < CommonScaffoldController
end
すべて app/views/common_scaffold/ の中のファイルを見るので、個別の Scaffold 用の Views ディレクトリ(app/views/users)は削除します。View ファイルを探す際に app/views/users/*, app/views/common_scaffold/* の順に探されるので消しておかないと app/views/common_scaffold/* のファイルを使ってくれません。
Model
デフォルトの Model ファイルのまま、特に変更する必要はありません。
class User < ActiveRecord::Base
end
まとめ
一部妄想で書いている部分があるので動かないところがあるかもしれませんが、大まかな考え方はこんな感じです。Viewファイル1セットを変えれば全て変わるので管理画面の変更も簡単です(^_^)※今回は rails generate scaffold したまんまの View を使いましたが、そのうち Ajax 版も作ってみたいと思います。
0 件のコメント:
コメントを投稿