railsのnested attributesの一通りの使い方


## バージョン
rails 4.2.1

## 概要
rails のnested attributesは、上手く使えばコードがごっそり減る素晴らしい機能だ。
しかし、どうもドキュメントが断片的で、包括したイケてる解説ページが無い。
なので、書きました。
異常系の動きが独特なのでハマる。

## ケーススタディ
※記憶を頼りに書いたので動作検証してません(ごめんなさい、余裕があったらやります)

ユーザー -1-(0..*)- 住所という関連があって、ユーザー情報を作成するコントローラーで住所も一緒に入力させます。

```ruby:schema.rb
ActiveRecord::Schema.define(version: 20160327072326) do
  create_table "addresses", force: :cascade do |t|
    t.string   "postal_code", limit: 255
    t.integer  "user_id",     limit: 4
  end
  create_table "users", force: :cascade do |t|
  end
end

```

```ruby:models
class User < ActiveRecord::Base
  has_many :addreses, dependent: :destroy, inverse_of: :user
  accepts_nested_attributes_for :addresses, allow_destroy: true, reject_if: :all_blank

end

class Address < ActiveRecord::Base
  belongs_to :user, inverse_of: :address
  validates :postal_code, format: { with: /¥A¥d{3}-?¥d{4}¥z/ }
end
```

```ruby:users_controller.rb
class UsersController < ApplicatonController

  def new
    @user = User.new
  end
  
  def create
    @user = User.new(user_params)
    if @user.save
      redirect_to root_path, notice: 'user created'
    else
      render :new
    end
  end
  
  private
    def user_params
      params.require(:user).permit(
        addresses_attributes: [
          :_destroy,
          :id,
          :postal_code,
        ],
      )
    end
end
```

```slim:users/_form.html.slim
= form_for @user do |f|
  - if @user.errors.any?
      h2 = "#{pluralize(@user.errors.count, "error")} prohibited this user from being saved:"
      ul
        - @user.errors.full_messages.each do |message|
          li = message

  div
    = f.label :addresses
    ol
      = f.fields_for :addresses, Address.new do |addresses_f|
        li
          table
            tr
              th = addresses_f.label      :postal_code
              td = addresses_f.text_field :postal_code

```

```yml:config/locales/ja.yml
ja:
  activerecord:
    attributes:
      address: &address
        postal_code: 郵便番号
      user:
      user/address: *address

```


## validation errorについて
postal_codeに 'hogehoge'など入力したとき、以下のようになる。

```ruby:
@user.errors[:addresses] #=> []
@user.errors['addresses.postal_code'] #=> [ 'は不正な値です' ]
@user.addresses.last.errors[:postal_code] #=> [ 'は不正な値です' ]

```

どうやら、小モデルのerrorsを集積して、同種のerrorはuniqして、ということを自動でやってくれてる模様。
field_with_errorsもちゃんと付けてくれる。最強！

### 小モデルのerrorsを集積してくれないことがある
どういう条件(多分何かしらの設定不備)で、やってくれたりやってくれなかったりするのかわからないのですが、たまにあります。
その場合は、以下のようになる

```ruby:設定がうまく言ってないとき
@user.errors[:addresses] #=> [ 'は不正な値です' ]
@user.errors['addresses.postal_code'] #=> []
@user.addresses.last.errors[:postal_code] #=> [ 'は不正な値です' ]

```

これだと、viewで子のerrorsを拾ってuniqして表示…とやらなければいけなくなって大変。何にミスったらこうなるのか知りたい。


## localeの設定
http://www.soulcutter.com/blog/2013/localization-of-nested-attributes-in-rails-3-dot-2/

nested attributesの場合は 'parent/child'というキーに定義すれば良いということが書いてある


### yamlのアンカーとエイリアス
http://magazine.rubyist.net/?0009-YAML#l2

'user/address'の情報と'address'の情報を２重管理するのが嫌な場合はyamlの機能を使ってまとめよう。

## 周辺知識
### inverse_of
http://railsguides.jp/association_basics.html#%E5%8F%8C%E6%96%B9%E5%90%91%E9%96%A2%E9%80%A3%E4%BB%98%E3%81%91

has_manyのオプションでこれを適切に指定すると、例えば

- userA
	- addressA
	- addressB

というとき、 addressAから見たuserAとaddressBから見たuserAが同一のインスタンスに見えてくれるらしい。

### strong parameterで_destroyだけでなくidも許可する理由
accepts_nested_attributes_forのallow_destroy: trueためのもの。
'_destroy'が必要なのは見れば分かるが、実は'id'も必要。ハマリポイント。