Rails 2.0のgenerateで複数形/単数形の違いを確認してみる
モデルは単数形でテーブル名は複数形だったかなとうろ覚えだったのだが、いざ使おうとするとやっぱり複数形/単数形の使い分けができてなかった。ちゃんと確認しておくメモ。
比較用のプロジェクトを生成
rails single rails plural
モデルの生成結果を比較
モデル名をtagとtagsとした場合を比較。
(cd single; script/generate model tag name:string) (cd plural; script/generate model tags name:string) diff -r single plural
セッションキー等の固有情報以外では、以下の2ファイルが違うことがわかった。
# diff -c single/app/models/tag.rb plural/app/models/tags.rb *** single/app/models/tag.rb 2007-12-19 22:08:23.000000000 +0900 --- plural/app/models/tags.rb 2007-12-19 22:08:35.000000000 +0900 *************** *** 1,2 **** ! class Tag < ActiveRecord::Base end --- 1,2 ---- ! class Tags < ActiveRecord::Base end # diff -c single/test/unit/tag_test.rb plural/test/unit/tags_test.rb *** single/test/unit/tag_test.rb 2007-12-19 22:08:23.000000000 +0900 --- plural/test/unit/tags_test.rb 2007-12-19 22:08:35.000000000 +0900 *************** *** 1,6 **** require File.dirname(__FILE__) + '/../test_helper' ! class TagTest < ActiveSupport::TestCase # Replace this with your real tests. def test_truth assert true --- 1,6 ---- require File.dirname(__FILE__) + '/../test_helper' ! class TagsTest < ActiveSupport::TestCase # Replace this with your real tests. def test_truth assert true
逆に、db/migration/001_create_tags.rb が両方にでき、内容は同じとなる。
コントローラの生成結果を比較
(cd single; script/generate controller tag) (cd plural; script/generate controller tags)
こんどはコントローラとそのテストとヘルパが作られる。
# diff -c single/app/controllers/tag_controller.rb plural/app/controllers/tags_controller.rb *** single/app/controllers/tag_controller.rb 2007-12-19 22:29:16.000000000 +0900 --- plural/app/controllers/tags_controller.rb 2007-12-19 22:29:27.000000000 +0900 *************** *** 1,2 **** ! class TagController < ApplicationController end --- 1,2 ---- ! class TagsController < ApplicationController end # diff -c single/test/functional/tag_controller_test.rb plural/test/functional/tags_controller_test.rb *** single/test/functional/tag_controller_test.rb 2007-12-19 22:29:16.000000000 +0900 --- plural/test/functional/tags_controller_test.rb 2007-12-19 22:29:27.000000000 +0900 *************** *** 1,6 **** require File.dirname(__FILE__) + '/../test_helper' ! class TagControllerTest < ActionController::TestCase # Replace this with your real tests. def test_truth assert true --- 1,6 ---- require File.dirname(__FILE__) + '/../test_helper' ! class TagsControllerTest < ActionController::TestCase # Replace this with your real tests. def test_truth assert true # diff -c single/app/helpers/tag_helper.rb plural/app/helpers/tags_helper.rb *** single/app/helpers/tag_helper.rb 2007-12-19 22:29:16.000000000 +0900 --- plural/app/helpers/tags_helper.rb 2007-12-19 22:29:27.000000000 +0900 *************** *** 1,2 **** ! module TagHelper end --- 1,2 ---- ! module TagsHelper end
至って平和ですなあ。
scaffoldの生成結果を比較
では、scaffoldでどうなるか。実は、ここでちとハマったので確認しておきたかったというのがこれまでの流れの背景。
rm -rf single plural rails single rails plural (cd single; script/generate scaffold tag name:string) (cd plural; script/generate scaffold tags name:string) diff -r single plural
まず、以下のファイルは上記で確認した結果と同じ。
Only in single/test/unit: tag_test.rb Only in plural/test/unit: tags_test.rb Only in single/app/models: tag.rb Only in plural/app/models: tags.rb
次、コントローラはファイル名が同じだが、内容ではモデルの単数形/複数形が違う。これに伴ってインスタンス変数の名前も違う。
diff -r single/app/controllers/tags_controller.rb plural/app/controllers/tags_controller.rb 5c5 < @tags = Tag.find(:all) --- > @tags = Tags.find(:all) 16c16 < @tag = Tag.find(params[:id]) --- > @tags = Tags.find(params[:id]) 20c20 < format.xml { render :xml => @tag } --- > format.xml { render :xml => @tags } 27c27 < @tag = Tag.new --- > @tags = Tags.new 31c31 < format.xml { render :xml => @tag } --- > format.xml { render :xml => @tags } 37c37 < @tag = Tag.find(params[:id]) --- > @tags = Tags.find(params[:id]) 43c43 < @tag = Tag.new(params[:tag]) --- > @tags = Tags.new(params[:tags]) 46,49c46,49 < if @tag.save < flash[:notice] = 'Tag was successfully created.' < format.html { redirect_to(@tag) } < format.xml { render :xml => @tag, :status => :created, :location => @tag } --- > if @tags.save > flash[:notice] = 'Tags was successfully created.' > format.html { redirect_to(@tags) } > format.xml { render :xml => @tags, :status => :created, :location => @tags } 52c52 < format.xml { render :xml => @tag.errors, :status => :unprocessable_entity } --- > format.xml { render :xml => @tags.errors, :status => :unprocessable_entity } 60c60 < @tag = Tag.find(params[:id]) --- > @tags = Tags.find(params[:id]) 63,65c63,65 < if @tag.update_attributes(params[:tag]) < flash[:notice] = 'Tag was successfully updated.' < format.html { redirect_to(@tag) } --- > if @tags.update_attributes(params[:tags]) > flash[:notice] = 'Tags was successfully updated.' > format.html { redirect_to(@tags) } 69c69 < format.xml { render :xml => @tag.errors, :status => :unprocessable_entity } --- > format.xml { render :xml => @tags.errors, :status => :unprocessable_entity } 77,78c77,78 < @tag = Tag.find(params[:id]) < @tag.destroy --- > @tags = Tags.find(params[:id]) > @tags.destroy
次にビューを見ると、インスタンス変数の名前の違いのほかに、ビューのおかれているディレクトリ名が両方とも複数形であることに気づく。コントローラ名が複数形に自動変換されている、ということらしい。手動でコントローラを作成した場合は複数形へ変換されなかったことからすると、挙動が違うことになる。
さらに驚くのが、複数形でscaffoldした場合の index.html.erb がエラーで動かないことだ。これは link_to で使う 〜_path の形のメソッドあるいは変数が未定義であることによる。ということで、scaffoldの引数に複数形を指定するとハマることになるようだ。
diff -r single/app/views/tags/edit.html.erb plural/app/views/tags/edit.html.erb 1c1 < <h1>Editing tag</h1> --- > <h1>Editing tags</h1> 3c3 < <%= error_messages_for :tag %> --- > <%= error_messages_for :tags %> 5c5 < <% form_for(@tag) do |f| %> --- > <% form_for(@tags) do |f| %> 16c16 < <%= link_to 'Show', @tag %> | --- > <%= link_to 'Show', @tags %> | diff -r single/app/views/tags/index.html.erb plural/app/views/tags/index.html.erb 8c8 < <% for tag in @tags %> --- > <% for tags in @tags %> 10,13c10,13 < <td><%=h tag.name %></td> < <td><%= link_to 'Show', tag %></td> < <td><%= link_to 'Edit', edit_tag_path(tag) %></td> < <td><%= link_to 'Destroy', tag, :confirm => 'Are you sure?', :method => :delete %></td> --- > <td><%=h tags.name %></td> > <td><%= link_to 'Show', tags %></td> > <td><%= link_to 'Edit', edit_tags_path(tags) %></td> > <td><%= link_to 'Destroy', tags, :confirm => 'Are you sure?', :method => :delete %></td> 20c20 < <%= link_to 'New tag', new_tag_path %> --- > <%= link_to 'New tags', new_tags_path %> diff -r single/app/views/tags/new.html.erb plural/app/views/tags/new.html.erb 1c1 < <h1>New tag</h1> --- > <h1>New tags</h1> 3c3 < <%= error_messages_for :tag %> --- > <%= error_messages_for :tags %> 5c5 < <% form_for(@tag) do |f| %> --- > <% form_for(@tags) do |f| %> diff -r single/app/views/tags/show.html.erb plural/app/views/tags/show.html.erb 3c3 < <%=h @tag.name %> --- > <%=h @tags.name %> 7c7 < <%= link_to 'Edit', edit_tag_path(@tag) %> | --- > <%= link_to 'Edit', edit_tags_path(@tags) %> |
テストのほうも、複数形で作成したscaffoldは失敗する。
diff -r single/test/functional/tags_controller_test.rb plural/test/functional/tags_controller_test.rb 15,17c15,17 < def test_should_create_tag < assert_difference('Tag.count') do < post :create, :tag => { } --- > def test_should_create_tags > assert_difference('Tags.count') do > post :create, :tags => { } 20c20 < assert_redirected_to tag_path(assigns(:tag)) --- > assert_redirected_to tags_path(assigns(:tags)) 23c23 < def test_should_show_tag --- > def test_should_show_tags 33,35c33,35 < def test_should_update_tag < put :update, :id => tags(:one).id, :tag => { } < assert_redirected_to tag_path(assigns(:tag)) --- > def test_should_update_tags > put :update, :id => tags(:one).id, :tags => { } > assert_redirected_to tags_path(assigns(:tags)) 38,39c38,39 < def test_should_destroy_tag < assert_difference('Tag.count', -1) do --- > def test_should_destroy_tags > assert_difference('Tags.count', -1) do
まとめ
- script/generate scaffoldでは複数形は避けること。
- コントローラを単数形にしたい場合は手動で作成すること。
ほんとかなあ・・・。