Nested Attributesって素晴らしそう。

複数のモデルを同時に登録する方法を模索した。

簡単にはできなさそうなオーラがある。

そんなこんなで調べるとNested AttributesとNested Model Formsを使って親子オブジェクトを一括で登録/変更するには(株式会社 万葉)が見つかる。
2.3でないとダメらしい。

もうちっと調べると、やりたかったことがそのまま書いてあるところを発見する。Nested Attributes with RJS(Do Ruby!)
 
さて、現状は2.2.2で2.3系にするのはまだ先になるので、併用を考える。

C:\Users\kenmituo>gem update rails
Updating installed gems
Updating rails
Successfully installed activesupport-2.3.4
Successfully installed activerecord-2.3.4
Successfully installed rack-1.0.1
Successfully installed actionpack-2.3.4
Successfully installed actionmailer-2.3.4
Successfully installed activeresource-2.3.4
Successfully installed rails-2.3.4
Gems updated: activesupport, activerecord, rack, actionpack, actionmailer, activ
eresource, rails

C:\Users\kenmituo>gem list rails

*** LOCAL GEMS ***

rails (2.3.4, 2.2.2)

既存のプロジェクトフォルダを丸ごとコピーして別名にする。
Aptana Studioで新しいプロジェクトを作成する。(over writeを聞かれるが全てNO)

config\enviroment.rbを修正する

# Specifies gem version of Rails to use when vendor/rails is not present
RAILS_GEM_VERSION = '2.3.4' unless defined? RAILS_GEM_VERSION

 
何も考えずにサーバを起動する

=> Booting Mongrel
=> Rails 2.3.4 application starting on http://127.0.0.1:3002
C:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/base.rb:2207
:in `class_of_active_record_descendant': undefined method `abstract_class?' for Object:Class (NoMethodError)
	from C:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/base.rb:1462:in `base_class'
	from C:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/base.rb:1138:in `reset_table_name'
	from C:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/base.rb:1134:in `table_name'
	from C:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/base.rb:3106:in `quoted_table_name'
	from C:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/base.rb:2226:in `sanitize_sql'
	from C:/Users/kenmituo/Documents/Aptana Studio/roommgr10/vendor/plugins/railswhere/lib/where.rb:169:in send'
	from C:/Users/kenmituo/Documents/Aptana Studio/roommgr10/vendor/plugins/railswhere/lib/where.rb:169:in 'initialize'
	from C:/Users/kenmituo/Documents/Aptana Studio/roommgr10/vendor/plugins/railswhere/lib/where.rb:156:in `new'	 ... 24 levels...
	from C:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `require'
	from ./script/server:3
	from -e:2:in `load'
	from -e:2

あれ?エラーになる。
 
railswhere が Rails 2.3.3 で動かない件(milk1000cc)を見つける。

vendor\plugins\railswhere\lib\where.rb
変更前:@criteria = ActiveRecord::Base.send(:sanitize_sql, criteria)
変更後:@criteria = ActiveRecord::Base.send(:sanitize_sql, criteria, nil)

これで動いた。
 

NoMethodError in Support/cableset#new
Showing app/views/support/cableset/_form.html.erb where line #14 raised:
undefined method `support_cableset_cablesethas_path' for #<ActionView::Base:0x5ce6da0>

2.3.2から「コントローラー_ほにゃらら_path」が無くなったとか、バグがあるとか。
 
とりあえず、バージョンを下げてみる

C:\Users\kenmituo\>gem install rails --version 2.3.3

 
変化が見られない。
 
form_forが悪いような気がした。サンプルの例を修正してみると、こんなかんじ。

変更前:<% form_for(@album) do |f| %>
変更後:<%- form_for :album, :url=>{:action=>:create, :id=>@album} do |f| -%>

画面は出るようになった。なんでだろう?まいっか。
 
 
つづき。
画面は表示されるようになったが「link_to_remote」の部分をクリックするとコンソールにエラーがでる。
モデルをサブディレクトリに格納しているのが悪そうだけど、解決策が分からない。

ActionView::TemplateError (Support::Cableset::Cablesetbes(#57766990) expected, got Array(#33640490)) on line #1 of app/views/support/cableset/add_cablesetbe.rjs:
1: @cableset = Support::Cableset::Cablesethas.new(params[:cableset])

子供のパラメータが入っているのがNGらしく、

params[:cableset].delete_if{|key, value| key == "cablesetbes"}

こんなことをやるとエラーは出なくなる。もちろん値は入らない。
 
無理矢理な感じをやってみる。 

temp = Hash.new
temp[:cablesetbes] = params[:cableset][:cablesetbes]
params[:cableset].delete_if{|key, value| key == "cablesetbes"}

@cableset = Support::Cableset::Cablesethas.new(params[:cableset])
@cableset.cablesetbes.build(temp[:cablesetbes])
@cableset.cablesetbes.build

パラメータ的には値が入っているけど、ビューで表示されない。
 
うーん・・・どうしよう?
ちょいと考え直して、モデルの入れ子を直して試す。

ActionView::TemplateError (Cablesetbe(#56904440) expected, got Array(#33640490)) on line #14 of app/views/support/cableset/add_cablesetbe.rjs:
14: @cableset = Cablesetha.new(params[:cableset])
15: @cableset.cablesetbes.build

    app/views/support/cableset/add_cablesetbe.rjs:14:in `new'
    app/views/support/cableset/add_cablesetbe.rjs:14:in `__instance_exec0'
    app/views/support/cableset/add_cablesetbe.rjs:1:in `_run_rjs_app47views47support47cableset47add_cablesetbe46rjs'
    -e:2:in `load'
    -e:2

変わらない。
 
 
 
ここでパラメータに再注目する。

"cablesetha"=>{"returnded_name"=>"", "returnded"=>"", "
lended"=>"", "cablesetbes"=>{{"room_name"=>"a", "room_time"=>"1"
}}, "memo"=>"", "item_name"=>"", "lended_name"=>"", "member_name"=>""}}

Module ActiveRecord::NestedAttributes::ClassMethodsこれを読む。
 
分かったこと
accepts_nested_attributes_forを使って親子関係を結んだ場合のパラメータは

親=>親パラメータ,子_attributes{子パラメータ}

これを実現するにはform_forを正しく書かないとダメ。

参考ページを修正するなら

変更前:<% form_for(@album) do |f| %>
変更後:<%- form_for @album, :url=>{:action=>:create} do |f| -%>

 
もう一つの注意点がパラメータ。
参考ページの「@album = Album.new」とやるなら問題なし。「@cableset = Cablesetha.new」とかやると間違える。
パラメータは「親モデルの名前」で届くのだ。

コントローラー
@cableset = Cablesetha.new
@cableset.cablesetbes.build

RJS
@cableset = Cablesetha.new(params[:cablesetha])
@cableset.cablesetbes.build

うまく動くと、_form.html.erbで描画されるHTMLソースは

<input id="cablesetha_cablesetbes_attributes_0_room_time" name="cablesetha[cablesetbes_attributes][0][room_time]" size="30" type="text" />

こんな感じになる。
 
 
後半は有給休暇中に書いた。仕事虫になっているな・・・俺。
 
そして、モデルを入れ子にしてみた。

class Support::Cableset::Cablesetha < ActiveRecord::Base
  set_table_name 'cablesethas'
  has_many :cablesetbes, :class_name=>'Support::Cableset::Cablesetbe'
  accepts_nested_attributes_for :cablesetbes ,:reject_if => proc{|attirbutes| attirbutes['member_name'].blank?}   
end

class Support::Cableset::Cablesetbe < ActiveRecord::Base
  set_table_name 'cablesetbes'
  belongs_to :cablesetha, :class_name=>'Support::Cableset::Cablesetha'
end

#RJSファイル
@cableset = Support::Cableset::Cablesetha.new(params[:support_cableset_cablesetha])
@cableset.cablesetbes.build
page.replace_html "cableset_form", :partial => 'form'

newとかパラメータが面倒になるけど、動いた。
 
当初の目的は果たせたよ。久しぶりの達成感!!