API dockや公式Githubに書いてあることそのままのタイトル
Rails6.1.4にて検証
経緯
対象となるModelは以下の通り
class TestModel < ActiveRecord::Base
validates_datetime :column_datetime
validates_date :column_date
end
RSpecのエラー調査をしている時にこんなソースを見た
it "short params is nil" do
expect(TestModel.new("column_date(1i)" => "2021", "column_date(2i)" => "13").\
to raise_error(ActiveRecord::MultiParameterAssignmentErrors)
end
テスト名でnil
として返ってくることを想定しているのに例外処理書いているのはさておき、
このMultiParameterAssignmentErrors
を当方見たことがなかったのでどういった時に出されるのかを調べてみた
調査
困ったときのAPI dock
Raised when there are multiple errors while doing a mass assignment through the {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=] method. The exception has an errors property that contains an array of AttributeAssignmentError objects, each corresponding to the error while assigning to an attribute.
要するにattributes=
による属性更新中に複数のエラーが発生した場合にこのエラーが出力されるとのこと
そのような状況は今までに何度も遭遇したのに見たことがないのはなぜ...?
実際に下記のような操作を行ってみたが確認できず
create_table(:users) do |t|
t.string :name
t.integer :age
end
User.new(name: 19, age: "name")
# => User(name: "19", age: 0)
以前、attributesは自動的にタイプキャストする仕様だと特定したことをすっかり忘れていた
このエラーが記載してあった箇所はDate等の時刻系であったことを思い出し、再度試してみる
TestModel.new("column_date(1i)" => "9999", "column_date(2i)" => "9999"
# => nil
どうも上手くいかない
行き詰ったため公式Githubのソースを読んでいたところ、このような記述を発見
def execute_callstack_for_multiparameter_attributes(callstack)
errors = []
callstack.each do |name, values_with_empty_parameters|
if values_with_empty_parameters.each_value.all?(NilClass)
values = nil
else
values = values_with_empty_parameters
end
send("#{name}=", values)
rescue => ex
errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name)
end
unless errors.empty?
error_descriptions = errors.map(&:message).join(",")
raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes [#{error_descriptions}]"
end
end
これで調べてみるとかなり類似する内容の記事を発見
assign_attributes
でタイプキャストされた後にvalues_with_empty_parameters.each_value.all?(NilClass)
でnil
か否かを判別する、このタイミングでエラーを発生させないとMultiParameterAssignmentErrors
は出力されなさそう
つまり?
タイプキャスト後にnilにならず例外的な処理になるような値を登録する必要があるらしい
そう考えると時刻系でよく発生するエラーであることが良くわかる
※見やすいように改行しています
TestModel.new("column_date(1i)" => "9999", "column_datetime(1i)" => "9999"
# => ActiveRecord::MultiparameterAssignmentErrors: 1 error(s)
# on assignment of multiparameter attributes [error on
# assignment [0] to column_datetime (Provided hash {1=>0}
# doesn't contain neccessary keys: [2, 3])]
やっと確認できた