accepts_nested_attributes_for

Windows7にRails2.3.4をインストールしましたので、
Rails2.3から導入されたaccepts_nested_attributes_forを使ってみました。
親子関係のあるレコードを同時に更新することができる便利な機能です。

いつもどおり新しいプロジェクトの上で行います。


1.プロジェクトの生成
NetBeansでメニューバーの[ファイル(F)]から[新規プロジェクト(W)]を選択します。
[ステップ1]プロジェクトを選択
 カテゴリ(C): Ruby
 プロジェクト(P): Ruby on Rails アプリケーション
[ステップ2]名前と場所
 プロジェクト名(N): Appli002
 プロジェクトの場所(l): D:\Rails_Project
 プロジェクトフォルダ(D): D:\Rails_Project\Appli002
 Rubyプラットフォーム(P): Ruby 1.8.7-p72
 サーバー(S): WEBrick
[ステップ3]データベース構成
 データベースアダプタ(P): mysql
 データベース名(D): Appli002_development
 ユーザー名(M): ******
 パスワード(W): ******
[ステップ4]Railsのインストール
 Railsのバージョン: 2.3.4


2.日本語環境の設定

/config/environment.rbと/app/controllers/application_controller.rbの一部を修正します。
Rails2.3からapplication.rbはapplication-controller.rbに名称が変更になりました。


3.データベースの作成

NetBeansで[Rake タスクを実行]を選択し、タスク一覧から[db:create]を実行します。


4.Scaffoldの実行
NetBeansでProjectとMemberで構成されるモデルをScaffoldで作成します。

(1) Projectの生成
ジェネレータ(G):scaffold
モデル(m):Project
属性ペア(A): p_name:string

(2) Memberの生成
ジェネレータ(G):scaffold
モデル(m):Member
属性ペア(A): m_name:string project_id:integer


5.マイグレーションの実行
NetBeansで[データベースマイグレーション]を選択し[現在のバージョンへ]を実行します。


6.動作確認

(1) 開発用WebサーバWEBrickの起動
NetBeansで[実行]を選択し実行します。

(2) ブラウザからプログラムを実行
URL http://127.0.0.1:3000/projects/を入力する。
    

URL http://127.0.0.1:3000/members/も試してみましょう。


7.モデルのリレーションの設定
いよいよaccepts_nested_attributes_forを使う場面がきました。
ProjectとMemberは1:nの関係であるとします。

(1) モデルProjectの修正

/app/models/project.rb

class Project < ActiveRecord::Base

has_many :members
accepts_nested_attributes_for :members,
:allow_destroy => true,
:reject_if => proc { |attributes| attributes['m_name'].blank? }

validates_presence_of :p_name
validates_uniqueness_of :p_name
end



(2) モデルMemberの修正

/app/models/member.rb

class Member < ActiveRecord::Base
belongs_to :project
end


8.コントローラの修正

/app/controllers/projects_controller.rb



# GET /projects/new
# GET /projects/new.xml
def new
@project = Project.new
5.times {@project.members.build}

respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => @project }
end
end


9.projectフォームの変更
(1)index

/app/views/projects/index.html.erb

<br/>
<h1><%= t('label.Listing_projects') %></h1>

<table>
<tr>
<th><%= t('label.P_name') %></th>
<th><%= t('label.M_name') %></th>
</tr>

<% @projects.each do |project| %>
<tr>
<td><%=h project.p_name %></td>
<td>
<% project.members.each do |m| %>
&#91;&nbsp;<%=h m.m_name %>&nbsp;&#93;&nbsp;
<% end %>
</td>

<td><%= link_to t('link.Show'), project %></td>
<td><%= link_to t('link.Edit'), edit_project_path(project) %></td>
<td><%= link_to t('link.Destroy'), project,
:confirm => t('label.Are_you_sure?'), :method => :delete %></td>
</tr>
<% end %>
</table>

<br />

<%= link_to t('link.New_project'), new_project_path %>



(2)new

/app/views/projects/new.html.erb

<br />
<h1>New project</h1>

<% form_for(@project) do |f| %>
<%= f.error_messages %%gt;

<p>
<%= f.label :p_name %><br />
<%= f.text_field :p_name %>
</p>
<p>
<%= f.label :m_name %><br />
<% f.fields_for :members do |m| %>
<p>
<%= m.text_field :m_name %><br />
</p>
<% end %>
</p>

<p>
<%= f.submit 'Create' %>
</p>
<% end %>

<%= link_to 'Back', projects_path %>



(3)show

/app/views/projects/show.html.erb

<p>
<b>P name:</b>
<%=h @project.p_name %>
</p>
<ol>
<% @project.members.each do |member| %>
<li>
<p>
<b>M name:</b>
<%=h member.m_name %>
</p>
</li>
<% end %>
</ol>

<%= link_to 'Edit', edit_project_path(@project) %> |
<%= link_to 'Back', projects_path %>



(4)edit

/app/views/projects/edit.html.erb

<br />
<h1>Editing project</h1>

<% form_for(@project) do |f| %>
<%= f.error_messages %>

<p>
<%= f.label :p_name %><br />
<%= f.text_field :p_name %>
</p>
<ol>
<% f.fields_for :members do |m| %>
<li>
<p>
<%= m.label :m_name %><br />
<%= m.check_box :_delete %><%= m.text_field :m_name %>
</p>
</li>
<% end %>
</ol>

<p>
<%= f.submit 'Update' %>
</p>
<% end %>

<%= link_to 'Show', @project %> |
<%= link_to 'Back', projects_path %>



10.動作確認

(1) WEBrickの再起動

(2) ブラウザからプログラムを実行
http://127.0.0.1/projects/
    


11.amatsuda-i18nのインストール(Railsの環境で一度やればよい)

ここまで来たのですから日本語化してみましょう。

コマンド プロンプト

D:\Rails_Project\appli001>gem so -a http://gems.github.com
http://gems.gethub.com added to sources

D:\Rails_Project\appli001>gem i amatsuda-i18n_generators
Successfully installed locale-2.0.5
Successfully installed getext-2.1.0
Successfully installed amatsuda-i18n_generators-0.6.0
3 gem installed
Installing ri documentation for locale-2.0.5...
Installing ri documentation for gettext-2.1.0...
Installing ri documentation for amatsuda-i18n_generators-0.6.0...
File not found: lib


12.日本語化

(1) i18nによる生成

コマンド プロンプト

D:\Rails_Project\proj001>ruby script/generate i18n ja
debug updating environment.rb ...
debug fetching ja.yml fro rails-i18n repository...
exists config/locales
update config/environment.rb
create config/locales/ja.yml
debug 2 models found.
debug 0 translation keys found in views.
debug translating activerecord.models.member ...
debug translating activerecord.models.porject ...
debug translating activerecord.attributes.member.m_name ...
debug translating activerecord.attributes.member.project ...
debug translating activerecord.attributes.project.p_name ...
debug translating activerecord.attributes.project.members ...
debug took 1.042 secs to translate.
create config/locales/translation_ja.yml


(2) ja.ymlの内容確認

/config/locales/ja.ymlを覗いてみてください。
いろいろなものが日本語に変換されることが解ります。
前のバージョンより変換項目が増えていますね。

/config/locales/ja.yml

# Japanese translations for Ruby on Rails
# by Akira Matsuda (ronnie@dio.jp)
# AR error messages are basically taken from Ruby-GetText-Package. Thanks to Masao Mutoh.
# updated for Rails 2.3.4 by Tsutomu Kuroda

ja:
date:
formats:
default: "%Y/%m/%d"
short: "%m/%d"
long: "%Y年%m月%d日(%a)"

day_names: [日曜日, 月曜日, 火曜日, 水曜日, 木曜日, 金曜日, 土曜日]
abbr_day_names: [日, 月, 火, 水, 木, 金, 土]

month_names: [~, 1月, 2月, 3月, 4月, 5月, 6月, 7月, 8月, 9月, 10月, 11月, 12月]
abbr_month_names: [~, 1月, 2月, 3月, 4月, 5月, 6月, 7月, 8月, 9月, 10月, 11月, 12月]

order: [:year, :month, :day]

time:
formats:
default: "%Y/%m/%d %H:%M:%S"
short: "%y/%m/%d %H:%M"
long: "%Y年%m月%d日(%a) %H時%M分%S秒 %Z"
am: "午前"
pm: "午後"

support:
array:
sentence_connector: "と"
skip_last_comma: true
words_connector: "と"
two_words_connector: "と"
last_word_connector: "と"

select:
prompt: "選択してください。"

number:
format:
separator: "."
delimiter: ","
precision: 3

currency:
format:
format: "%n%u"
unit: "円"
separator: "."
delimiter: ","
precision: 0

percentage:
format:
delimiter: ""

precision:
format:
delimiter: ""

human:
format:
delimiter: ""
precision: 1
storage_units:
format: "%n%u"
units:
byte: "バイト"
kb: "キロバイト"
mb: "メガバイト"
gb: "ギガバイト"
tb: "テラバイト"

datetime:
distance_in_words:
half_a_minute: "30秒前後"
less_than_x_seconds:
one: "1秒以内"
other: "{{count}}秒以内"
x_seconds:
one: "1秒"
other: "{{count}}秒"
less_than_x_minutes:
one: "1分以内"
other: "{{count}}分以内"
x_minutes:
one: "1分"
other: "{{count}}分"
about_x_hours:
one: "約1時間"
other: "約{{count}}時間"
x_days:
one: "1日"
other: "{{count}}日"
about_x_months:
one: "約1ヶ月"
other: "約{{count}}ヶ月"
x_months:
one: "1ヶ月"
other: "{{count}}ヶ月"
about_x_years:
one: "約{{count}}年"
other: "約{{count}}年"
over_x_years:
one: "{{count}}年以上"
other: "{{count}}年以上"

activerecord:
errors:
template:
header:
one: "{{model}}にエラーが発生しました。"
other: "{{model}}に{{count}}つのエラーが発生しました。"
body: "次の項目を確認してください。"

messages:
inclusion: "は一覧にありません。"
exclusion: "は予約されています。"
invalid: "は不正な値です。"
confirmation: "が一致しません。"
accepted: "を受諾してください。"
empty: "を入力してください。"
blank: "を入力してください。"
too_long: "は{{count}}文字以内で入力してください。"
too_short: "は{{count}}文字以上で入力してください。"
wrong_length: "は{{count}}文字で入力してください。"
taken: "はすでに存在します。"
not_a_number: "は数値で入力してください。"
greater_than: "は{{count}}より大きい値にしてください。"
greater_than_or_equal_to: "は{{count}}以上の値にしてください。"
equal_to: "は{{count}}にしてください。"
less_than: "は{{count}}より小さい値にしてください。"
less_than_or_equal_to: "は{{count}}以下の値にしてください。"
odd: "は奇数にしてください。"
even: "は偶数にしてください。"

full_messages:
format: "{{attribute}}{{message}}"


(3) translation_ja.ymlの内容確認

/config/locales/translation_ja.yml

ja:
activerecord:
models:
member: "会員" #g
project: "プロジェクト" #g

attributes:
member:
m_name: "メートルの名前" #g
project: "プロジェクト" #g

project:
p_name: "P名前" #g
members: "メンバー" #g


13.ビューの修正

(1) translation_ja.ymlの追加修正

/config/locales/translation_ja.yml

ja:
activerecord:
models:
member: "メンバー" #g
project: "プロジェクト" #g

attributes:
member:
m_name: "メンバー名" #g
project: "プロジェクト" #g
project_id: "プロジェクト"
members_m_name: "メンバー名"
project:
p_name: "プロジェクト名" #g
members: "メンバー" #g


label:
Editing_project: プロジェクトの更新
Listing_projects: プロジェクト一覧
New_project: プロジェクトの新規登録
P_name: プロジェクト名
M_name: メンバー名
Are_you_sure?: 削除してよろしいですか?

link:
New_project: 追加
Show: 表示
Edit: 編集
Destroy: 削除
Back: 前画面

button:
Create: 新規登録実行
Update: 更新実行



(2)ビューindexの修正

/app/views/projects/index.html.erb

<br />
<h1><%= t('label.Listing_projects') %<>/h1<

<table>
<tr>
<th><%= t('label.P_name') %></th>
<th><%= t('label.M_name') %></th>
</tr>

<% @projects.each do |project| %>
<tr>
<td><%=h project.p_name %></td>
<td>
<% project.members.each do |member| %>
&#91;&nbsp;<%=h member.m_name %>&nbsp;&#93;&nbsp;
<% end %>
</td>
<td><%= link_to t('link.Show'), project %></td>
<td><%= link_to t('link.Edit'), edit_project_path(project) %></td>
<td><%= link_to t('link.Destroy'), project, :confirm => t('label.Are_you_sure?'), :method => :delete %></td>
</tr>
<% end %>
</table>

<br />

<%= link_to t('link.New_project'), new_project_path %>


(3)ビューnewの修正

/app/views/projects/new.html.erb

<br/>
<h1><%= t('label.New_project') %></h1>

<% form_for(@project) do |f| %>
<%= f.error_messages %>

<p>
<%= f.label t('label.P_name') %><br />
<%= f.text_field :p_name %>
</p>
<p>
<%= f.label t('label.M_name') %><br />
<% f.fields_for :members do |m| %>
<p>
<%= m.text_field :m_name %><br />
</p>
<% end %>
</p>
<p>
<%= f.submit t('button.Create') %>
</p>
<% end %>

<%= link_to t('link.Back'), projects_path %>


(4)ビューshowの修正

/app/views/projects/show.html.erb

<p>
<b><%= t('label.P_name') %>:</b>
<%=h @project.p_name %>
</p>
<ol>
<% @project.members.each do |member| %>
<li>
<p>
<b><%= t('label.M_name') %>:</b>
<%=h member.m_name %>
</p>
</li>
<% end %>
</ol>

<%= link_to t('link.Edit'), edit_project_path(@project) %> |
<%= link_to t('link.Back'), projects_path %>


(5)ビューeditの修正

/app/views/projects/edit.html.erb

<br />
<h1><%= t('label.Editing_project') %></h1>

<% form_for(@project) do |f| %>
<%= f.error_messages %>

<p>
<%= f.label t('label.P_name') %><br />
<%= f.text_field :p_name %>
</p>
<ol>
<% f.fields_for :members do |m| %>
<li>
<p>
<%= m.label t('label.M_name') %><br />
<%= m.check_box :_delete %><%= m.text_field :m_name %>
</p>
</li>
<% end %>
</ol>
<p>
<%= f.submit t('button.Update') %>
</p>
<% end %>

<%= link_to t('link.Show'), @project %> |
<%= link_to t('link.Back'), projects_path %>


14.動作確認

(1) WEBrickの再起動

(2) ブラウザからプログラムを実行
http://127.0.0.1/projects/