Rails autosaveオプションの検証記録

はじめに
--------
モデル間をアソシエーションしている際、関連づけられているモデルオブジェクトを自動的に保存したり、保存しないようにするには`autosave`オプションを利用します。

このオプションの挙動について、下記のサンプルを用いて検証をしました。

サンプル
--------

**モデル**

CustomerモデルとAddressモデルを1対1で関連づけしています。

```ruby:app/models/customer.rb
class Customer < ActiveRecord::Base
  has_one :address, dependent: :destroy

  validates :name, presence: true
end
```

```ruby:app/models/address.rb
class Address < ActiveRecord::Base
  belongs_to :customer

  validates :address, presece: true
end
```

**フォーム**

関連づけられたモデルを一つの画面で登録したり、編集したりするため、フォームオブジェクトを作成します。

```bury:app/forms/customer_form.rb
class CustomerForm
  include ActiveModel::Model

  attr_accessor :customer
  delegate :persisted?, to: :customer

  def initialize(customer = nil)
    @customer = customer
    @customer ||= Customer.new
    @customer.build_address unless @customer.address
  end

  def assign_attributes(params = {})
    @params = params

    customer.assign_attributes(customer_params)
    customer.address.assign_attributes(address_params)
  end

  def save
    customer.save
  end

  private
  def customer_params
    @params.require(:customer).permit(:name)
  end

  def address_params
    @params.require(:address).permit(:address)
  end
end
```

**ビュー**

パーシャルをつかってビューを作成します。

```erb:app/views/customers/new.html.erb
<% @title = '顧客の新規登録' %>
<h1><%= @title %></h1>

<div id="generic-form">
  <%= form_for @customer_form, as: 'form', url: :customers do |f| %>
    <%= render 'form', f: f %>
    <div class="buttons">
      <%= f.submit '登録' %>
      <%= link_to 'キャンセル', :customers %>
    </div>
  <% end %>
</div>
```

```erb:app/views/customers/edit.html.erb
<% @title = '顧客アカウントの編集' %>
<h1><%= @title %></h1>

<div id="generic-form">
  <%= form_for @customer_form, as: 'form',
    url: [ @customer_form.customer ] do |f| %>
    <%= render 'form', f: f %>
    <div class="buttons">
      <%= f.submit '更新' %>
      <%= link_to 'キャンセル', :customers %>
    </div>
  <% end %>
</div>
```

```erb:app/views/customers/_form.html.erb
<fieldset id="customer-fields">
  <legend>基本情報</legend>
  <%= render 'customer_fields', f: f %>
</fieldset>
<fieldset id="address-fields">
  <legend>自宅住所</legend>
  <%= render 'address_fields', f: f %>
</fieldset>
```

```erb:app/views/customers/_customer_fields.html.erb
<%= f.fields_for :customer, f.object.customer do |ff| %>
<div>
  <%= ff.label :name, '氏名', class: 'required' %>
  <%= ff.text_field :name, required: true %>
</div> 
```

```erb:app/views/customers/_address_fields.html.erb
<%= f.fields_for :address, f.object.customer.address do |ff| %>
<div>
  <%= ff.label :address, '住所', class: 'required' %>
  <%= ff.text_field :address, required: true %>
</div>
```

コントローラ
---------------

新規登録と編集用のコントローラを作成します。

```ruby:app/controllers/customers_controller.rb
class CustomersController < ApplicationController
  def new
    @customer_form = CustomerForm.new
  end

  def edit
    @customer_form = CustomerForm.new(Customer.find(params[:id]))
  end

  def create
    @customer_form = CustomerForm.new
    @customer_form.assign_attributes(params[:form])
    if @customer_form.save
      flash.notice = '顧客を追加しました。'
      redirect_to action: 'index'
    else
      flash.now.alert = '入力に誤りがあります。'
      render action: 'new'
    end
  end

  def update
    @customer_form = CustomerForm.new(Customer.find(params[:id]))
    @customer_form.assign_attributes(params[:form])
    if @customer_form.save
      flash.notice = '顧客情報を更新しました。'
      redirect_to action: 'index'
    else
      flash.now.alert = '入力に誤りがあります。'
      render action: 'edit'
    end
  end
end
```

検証1(autosaveオプションなし)
--------------------------------

モデルにautosaveオプションを利用しない場合(つまり上記サンプルの状態)の検証です。

* 新規登録の場合: CustomerオブジェクトとAddressオブジェクトの両方がデータベースに保存される。
* 更新の場合: Csutomerオブジェクトの変更のみデータベースに保存され、Addressオブジェクトの変更はデータベースに保存されない。

検証2(autosaveオプションにtrueを指定)
------------------------------------

モデルにautosaveオプションをtrueで指定した場合の検証です。

```ruby:app/models/customer.rb
class Customer < ActiveRecord::Base
  has_one :address, dependent: :destroy, autosave: true

  validates :name, presence: true
end
```

* 新規登録の場合: CustomerオブジェクトとAddressオブジェクトの両方がデータベースに保存される。
* 更新の場合: CustomerオブジェクトとAddressオブジェクトの両方の変更がデータベースに保存される。

検証3(autosaveオプションにfalseを指定)
------------------------------------

モデルにautosaveオプションをfalseで指定した場合の検証です。

```ruby:app/models/customer.rb
class Customer < ActiveRecord::Base
  has_one :address, dependent: :destroy, autosave: false

  validates :name, presence: true
end
```

* 新規登録の場合: Customerオブジェクトのみデータベースに保存され、Addressオブジェクトはデータベース保存されない。
* 更新の場合: Customerオブジェクトの変更のみデータベースに保存され、Addressオブジェクトの変更はデータベースに保存されない。