respond_with について調べてみた


## きっかけ
`inherited_resources`を導入して、`scaffold`で生成した`spec`の中、`POST create invalid`と`PUT update invalid`と`update`のケースがエラーになったので、調べてみました

## 環境
- Rails 4.1.4
- Ruby 2.1.2

## エラーのケース

```
it "re-renders the 'new' template" do
  # Trigger the behavior that occurs when invalid params are submitted
  Journal.any_instance.stub(:save).and_return(false)
  post :create, {:journal => { "title" => "invalid title" }}, valid_session
  response.should render_template(:new)
end

it "re-renders the 'edit' template" do
  journal = Journal.create! valid_attributes
  # Trigger the behavior that occurs when invalid params are submitted
  Journal.any_instance.stub(:save).and_return(false)
  put :update, {:id => journal.to_param, :journal => { "title" => "invalid title" }}, valid_session
  response.should render_template(:edit)
end

it "updates the requested journal" do
  journal = Journal.create! valid_attributes
  # Assuming there are no other journals in the database, this
  # specifies that the Journal created on the previous line
  # receives the :update_attributes message with whatever params are
  # submitted in the request.
  Journal.any_instance.should_receive(:update).with({ "title" => "MyString" })
  put :update, {:id => journal.to_param, :journal => { "title" => "MyString" }}, valid_session
end
```

### 内容
```bash
Failures:

  1) JournalsController PUT update with invalid params re-renders the 'edit' template
     Failure/Error: response.should render_template(:edit)
       expecting <"edit"> but rendering with <[]>
     # ./spec/controllers/journals_controller_spec.rb:122:in `block (4 levels) in <top (required)>'
     # -e:1:in `<main>'

  2) JournalsController POST create with invalid params re-renders the 'new' template
     Failure/Error: response.should render_template(:new)
       expecting <"new"> but rendering with <[]>
     # ./spec/controllers/journals_controller_spec.rb:78:in `block (4 levels) in <top (required)>'
     # -e:1:in `<main>'

  3) JournalsController PUT update with valid params updates the requested journal
     Failure/Error: Unable to find matching line from backtrace
       Exactly one instance should have received the following message(s) but didn't: update
```

## エラーの原因
### 1,2 の原因
- `inherited_resources`は`respond_with`メソッドを活用している
- `respond_with`は`Responder`の`call`⇒`to_html`⇒`navigation_behavior`の順んで呼び出される

```ruby:https://github.com/rails/rails/blob/master/actionpack/lib/action_controller/metal/responder.rb#L192
def navigation_behavior(error)
  if get?
    raise error
  elsif has_errors? && default_action
    render :action => default_action
  else
    redirect_to navigation_location
  end
end
...
def has_errors?
  resource.respond_to?(:errors) && !resource.errors.empty?
end
```

- エラーがあるかどうかの判定は、`resource.errors.empty?`で判定している
- そして、`spec`の中は、`Journal.any_instance.stub(:save).and_return(false)`のように`stub`でエラーにさせようとしているが、上記の判定条件に引っかからない為、最後の`redirect_to navigation_location`に至ってしまう流れです。

### 3の原因
- `inherited_resources`はまだ`update_attributes`を使っているので、それに合わせれば良い

## 対応方法
- `spec`の`stub`をやめて、自分のプロジェクトのロジックをみてちゃんと`invalid`のパラメータを渡せばよい
- うちの場合、`Journal`の`title`が必須入力なのんで、`title`を`nil`に渡すように修正すれば、全`spec`パスできました。

```
it "re-renders the 'new' template" do
  # Trigger the behavior that occurs when invalid params are submitted
  # Journal.any_instance.stub(:save).and_return(false)
  post :create, {:journal => { "title" => nil }}, valid_session
  response.should render_template(:new)
end

it "re-renders the 'edit' template" do
  journal = Journal.create! valid_attributes
  # Trigger the behavior that occurs when invalid params are submitted
  # Journal.any_instance.stub(:save).and_return(false)
  put :update, {:id => journal.to_param, :journal => { "title" => nil }}, valid_session
  response.should render_template(:edit)
end

it "updates the requested journal" do
  journal = Journal.create! valid_attributes
  # Assuming there are no other journals in the database, this
  # specifies that the Journal created on the previous line
  # receives the :update_attributes message with whatever params are
  # submitted in the request.
  Journal.any_instance.should_receive(:update_attributes).with({ "title" => "MyString" })
  put :update, {:id => journal.to_param, :journal => { "title" => "MyString" }}, valid_session
end
```

## 参考リンク
[respond_to について調べてみた](http://dev.classmethod.jp/server-side/rails-respond_to/)

[josevalim/inherited_resources](https://github.com/josevalim/inherited_resources)