もう一つのmultiparameter assignment を応用したフォームの入力
「multiparameter assignment を応用したフォームの入力」より。
問題
以下のようなフォームをRailsでうまく扱う。
Viewには次のように記述。
<input type="checkbox" name="user[interests(1s)]" value="コンピュータ"/>コンピュータ
<input type="checkbox" name="user[interests(2s)]" value="旅行" />旅行
<input type="checkbox" name="user[interests(3s)]" value="車" />車
こんなやり方も?
元のActiveRecord::Base#execute_callstack_for_multiparameter_attributes関数を読んでみる。
def execute_callstack_for_multiparameter_attributes(callstack)
errors = []
callstack.each do |name, values|
klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass
if values.empty?
send(name + "=", nil)
else
begin
send(name + "=", Time == klass ? (@@default_timezone == :utc ? klass.utc(*values) : klass.local(*values)) : klass.new(*values))
rescue => ex
errors << AttributeAssignmentError.new("error on assignment #{values.inspect} to #{name}", ex, name)
end
end
end
unless errors.empty?
raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes"
end
end
ここで注目したいのは、klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attributeと、klass.new(*values)。
ActiveRecord::Base.reflect_on_aggregation()はどうやら関連や合成(Composed)クラスを取得してくるようだ。そこでnameに関連づけられたクラスをklass変数に保持して、klass.new(*values)で生成すると。
「RailsでUglyになんとかTimeZoneをサポートする」でもうまくいってたので、こんな感じでもうまくいきそうだ。
class User < ActiveRecord::Base
composed_of :interest_list, :class_name => 'Array'
def interest_list=(list)
interests = list.join('|')
end
end
元のActiveRecord::Base#execute_callstack_for_multiparameter_attributes関数を上書きしたりしなくてすむのでこれでも良いかなと思ったりしないでも無い。
勘違い
すでに元記事の方でフォローされてるが、Array.new(item,...])というコンストラクタが無いため、上記の方法はうまくいかない。このサンプルコードを書くまえにちらっとArrayのリファレンスを見たんだけど、Array[item,...]をArray.new(item,...)と見間違えたらしい^^;
という言い訳などを…。
人のあら探ししてる暇あったら自分でちゃんと検証してみましょうね!