0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

MultiParameterAssignmentErrorsは複数の属性でエラーが発生した際に出力される

Last updated at Posted at 2021-08-06

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のソースを読んでいたところ、このような記述を発見

rails/activerecord/lib/active_record/attribute_assignment.rb
      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])]

やっと確認できた

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?