has_many な accepts_nested_attributes_for のバリデーションエラーに index 追加するオプション


最近 [rails5 の activerecord の changelog](https://github.com/rails/rails/blob/v5.0.0.beta1/activerecord/CHANGELOG.md) を眺めてます。

で、「has_many な accepts_nested_attributes_for のバリデーションエラーに index 追加するオプション」の話。

例えば

```rb
class User < ActiveRecord::Base
  has_many :conditions
  accepts_nested_attributes_for :conditions
end

class Condition < ActiveRecord::Base
  belongs_to :user
  validates :value, presence: true
end
```

みたいな has_many な関係の nested attributes の時。

```irb:rails4
cirb(main):002:0> user = User.find(4)
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 4]]
=> #<User id: 4, gender: "woman", age: 11, created_at: "2016-02-25 13:11:13", updated_at: "2016-02-25 13:11:13">

irb(main):003:0> params = {user: { age: 12, conditions_attributes: [{value: 'ok'}, {value: ''}, {value: 'bad'}]}}
=> {:user=>{:age=>12, :conditions_attributes=>[{:value=>"ok"}, {:value=>""}, {:value=>"bad"}]}}

irb(main):004:0> user.attributes = params[:user]
=> {:age=>12, :conditions_attributes=>[{:value=>"ok"}, {:value=>""}, {:value=>"bad"}]}

irb(main):005:0> user.valid?
=> false

irb(main):006:0> user.errors
=> #<ActiveModel::Errors:0x007fe0c3ced6b8 @base=#<User id: 4, gender: "woman", age: 12, created_at: "2016-02-25 13:11:13", updated_at: "2016-02-25 13:11:13">, @messages={:"conditions.value"=>["can't be blank"]}>
```

こんな感じでバリデーションエラーが出た時も nested attributes でエラーになったのはわかるけど、何番目なのか？ がわからなくて不親切でした。

実は rails5 でも、そのままだと状況は同じです。

```irb:rails5
irb(main):001:0> user = User.find(5)
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 5], ["LIMIT", 1]]
=> #<User id: 5, gender: "woman", age: 60, created_at: "2016-02-25 13:11:13", updated_at: "2016-02-25 13:11:13">
irb(main):002:0> params = {user: {age: 61, conditions_attributes: [{value: 'good'}, {value: ''}, {value: 'bad'}]}}
=> {:user=>{:age=>61, :conditions_attributes=>[{:value=>"good"}, {:value=>""}, {:value=>"bad"}]}}
irb(main):003:0> user.attributes = params[:user]
=> {:age=>61, :conditions_attributes=>[{:value=>"good"}, {:value=>""}, {:value=>"bad"}]}
irb(main):004:0> user.valid?
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 5], ["LIMIT", 1]]
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 5], ["LIMIT", 1]]
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 5], ["LIMIT", 1]]
=> false
irb(main):005:0> user.errors
=> #<ActiveModel::Errors:0x007f96523c4070 @base=#<User id: 5, gender: "woman", age: 61, created_at: "2016-02-25 13:11:13", updated_at: "2016-02-25 13:11:13">, @messages={:"conditions.value"=>["can't be blank"]}, @details={"conditions.value"=>[{:error=>:blank}]}>
```

ところが、 has_many の定義で以下のように

```rb
class User < ApplicationRecord
  has_many :conditions, index_errors: true
  accepts_nested_attributes_for :conditions
end
```

`index_errors` オプションを指定すると。

こんなふうに

```irb:rails5-index_errors付き
irb(main):001:0> user = User.find(5)
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 5], ["LIMIT", 1]]
=> #<User id: 5, gender: "woman", age: 60, created_at: "2016-02-25 13:11:13", updated_at: "2016-02-25 13:11:13">

irb(main):002:0> params = {user: {age: 61, conditions_attributes: [{value: 'good'}, {value: ''}, {value: 'bad'}]}}
=> {:user=>{:age=>61, :conditions_attributes=>[{:value=>"good"}, {:value=>""}, {:value=>"bad"}]}}

irb(main):003:0> user.attributes = params[:user]
=> {:age=>61, :conditions_attributes=>[{:value=>"good"}, {:value=>""}, {:value=>"bad"}]}
irb(main):004:0> user.valid?
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 5], ["LIMIT", 1]]
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 5], ["LIMIT", 1]]
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 5], ["LIMIT", 1]]
=> false

irb(main):005:0> user.errors
=> #<ActiveModel::Errors:0x007f9652ba16a8 @base=#<User id: 5, gender: "woman", age: 61, created_at: "2016-02-25 13:11:13", updated_at: "2016-02-25 13:11:13">, @messages={:"conditions[1].value"=>["can't be blank"]}, @details={"conditions[1].value"=>[{:error=>:blank}]}>
```

messages や details の key が `conditions[1].value` になってる！

今までこれができなくて nested attributes 面倒とか思ってたんですが、便利になったかも。

詳細は、該当の差分見るのが多分一番はやいです。
https://github.com/rails/rails/commit/118232aef4094ce0a1dfbb827b672509e7c33542