35歳だけどRailsチュートリアルやってみた。[第4版 10章 10.1 ユーザーを更新する まとめ&解答例]

#はじめに
最近、プロジェクト管理業務が業務の大半を占めており、
プログラムを書く機会がなかなかありません。

このままだとプログラムがまったく書けない人になってしまう危機感（迫り来る35歳定年説）と、
新しいことに挑戦したいという思いから、
[Ruby on Rails チュートリアル実例を使ってRailsを学ぼう 第4版](https://railstutorial.jp/)を学習中です。
業務で使うのはもっぱらJavaなのですが、Rails楽しいですね。


これまでEvernoteに記録していましたが、ソースコードの貼付けに限界を感じたため、
Qiitaで自分が学習した結果をアウトプットしていきます。

個人の解答例なので、誤りがあればご指摘ください。

##動作環境
- cloud9
- ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-linux]
- Rails 5.0.0.1

##10.1.1 編集フォーム
###本章での学び
####Usersコントローラに、editアクションを追加する
`params[:id]`で、ユーザーのidを取り出す。

```/sample_app/app/controllers/users_controller.rb
def edit
  @user = User.find(params[:id])
end
```

####editビューを実装する
editビューは、新規ユーザ登録のnewビューとほぼ同じ。
`form_for(@user)`を使って実装する。

####PATCHリクエストを送信する
`form_for(@user)`を使うと、HTML生成時にrailsが自動的に、`<input type="hidden" name="_method" value="patch">`を追加してくれる。

自動生成されたHTMLは以下の通り。

```html
<form class="edit_user" id="edit_user_1" action="/users/1" accept-charset="UTF-8" method="post">
<input name="utf8" type="hidden" value="✓">
<input type="hidden" name="_method" value="patch">
・・・略・・・
</form>
```

これは、ブラウザがPATCHリクエストを送信することができないため、POSTリクエストとhidden項目を使って、PATCHリクエストに偽造している。


 
###演習1
*先ほど触れたように、target="_blank"で新しいページを開くときには、セキュリティ上の小さな問題があります。それは、リンク先のサイトがHTMLドキュメントのwindowオブジェクトを扱えてしまう、という点です。具体的には、フィッシング (Phising) サイトのような、悪意のあるコンテンツを導入させられてしまう可能性があります。Gravatarのような著名なサイトではこのような事態は起こらないと思いますが、念のため、このセキュリティ上のリスクも排除しておきましょう。対処方法は、リンク用のaタグのrel (relationship) 属性に、"noopener"と設定するだけです。早速、リスト 10.2で使ったGravatarの編集ページへのリンクにこの設定をしてみましょう。*

`noopener`をつけないリスクは、下記参照。
[リンクのへの rel=noopener 付与による Tabnabbing 対策](https://blog.jxck.io/entries/2016-06-12/noopener.html)

```/sample_app/app/views/users/edit.html.erb
      <a href="http://gravatar.com/emails" target="_blank" rel="noopener">change</a>
```


###演習2
*リスト 10.5のパーシャルを使って、new.html.erbビュー (リスト 10.6) とedit.html.erbビュー (リスト 10.7) をリファクタリングしてみましょう (コードの重複を取り除いてみましょう)。ヒント: 3.4.3で使ったprovideメソッドを使うと、重複を取り除けます3。(関連するリスト 7.27の演習課題を既に解いている場合、この演習課題をうまく解けない可能性があります。うまく解けない場合は、既存のコードのどこに差異があるのか考えながらこの課題に取り組んでみましょう。例えば筆者であれば、リスト 10.5のテクニックをリスト 10.6に適用してみたり、リスト 10.7のテクニックをリスト 10.5に適用してみたりするでしょう。)*

リファクタリングを実施する。
※演習7.27を実施している状態です。

重複部分をformパーシャルとして切り出す。

```/sample_app/app/views/users/_form.html.erb
    <%= form_for(@user, url: yield(:url)) do |f| %>
      <%= render 'shared/error_messages', object:@user %>
      
      <%= f.label :name %>
      <%= f.text_field :name, class: 'form-control' %>
      
      <%= f.label :email %>
      <%= f.email_field :email, class: 'form-control' %>

      <%= f.label :password %>
      <%= f.password_field :password, class: 'form-control' %>

      <%= f.label :password_confirmation, "Confirmation" %>
      <%= f.password_field :password_confirmation, class: 'form-control' %>

      <%= f.submit yield(:buttun_text), class: "btn btn-primary" %>
    <% end %>
```

新規ユーザー登録画面
`render 'form'`で、formパーシャルを呼び出す。

```/sample_app/app/views/users/new.html.erb
<% provide(:title, 'Sign up')%>
<% provide(:buttun_text, 'Create my account') %>
<% provide(:url, signup_path) %>
<h1>Sign up</h1>

<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <%= render 'form'%>
  </div>
</div>
```

ユーザー更新画面
`url:user_path`を使う理由は、PATCHリクエストと、usersコントローラのupdateアクションが紐づいているため。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/98130/3e4c75a4-751f-bfe4-45bb-f0db510637a7.png)

```/sample_app/app/views/users/edit.html.erb
<% provide(:title, "Edit user") %>
<% provide(:buttun_text, "Save changes")%>
<% provide(:url, user_path) %>
<h1>Update your profile</h1>

<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <%= render 'form' %>
    <div class="gravatar_edit">
      <%= gravatar_for @user %>
      <a href="http://gravatar.com/emails" target="_blank">change</a>
    </div>
  </div>
</div>
```

この時点では、まだupdateアクションを実装していないため、ユーザ―を更新するとエラーになる。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/98130/57b195c2-9de9-87f2-24c1-b7015a93b633.png)

##10.1.2 編集の失敗
###本章での学び
####【Controller】updateアクションの作成
Controller内でのみ使用できる、プライベートなStrong parametersの`user_params`を使って、ユーザー情報を更新する。

```/sample_app/app/controllers/users_controller.rb
  def update
    @user = User.find(params[:id])
    if @user.update_attributes(user_params)
      # 更新に成功した場合を扱う。
    else
      render 'edit'
    end
  end
```

###演習1
*編集フォームから有効でないユーザー名やメールアドレス、パスワードを使って送信した場合、編集に失敗することを確認してみましょう。*

すべてブランクの場合、バリデーションエラーになることを確認。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/98130/fca1fec5-82e4-bdce-1c30-15cebcd06403.png)


##10.1.3 編集失敗時のテスト
###本章での学び
####統合テストの生成
`rails generate`コマンドで自動生成する。

```
yokoyan:~/workspace/sample_app (master) $ rails generate integration_test users_edit
Running via Spring preloader in process 2388
Expected string default value for '--jbuilder'; got true (boolean)
      invoke  test_unit
      create    test/integration/users_edit_test.rb
```

####テストコードの実装
以下のテストをコード化する。

- Fixtureでテストユーザmichealを作成する
- michealのユーザ情報を、GETリクエストで、users#editアクションに送信する
- editビューが描画されること
- 無効なユーザ情報を、PATCHリクエストで、users#updateアクションに送信する
- editビューが再描画されること

`rails routes`でパス情報を確認

```irb
yokoyan:~/workspace/sample_app (master) $ rails routes
   Prefix Verb   URI Pattern               Controller#Action
edit_user GET    /users/:id/edit(.:format) users#edit
     user GET    /users/:id(.:format)      users#show
          PATCH  /users/:id(.:format)      users#update
```

テストコードの実装

```/sample_app/test/integration/users_edit_test.rb
  def setup
    @user = users(:michael)
  end
  
  test "unsuccessful edit" do
    get edit_user_path(@user)
    assert_template 'users/edit'
    patch user_path(@user), params: { user: { name: "",
                                              email: "foo@invalid",
                                              password: "foo",
                                              password_confirmation: "bar" } }
    assert_template 'users/edit'
  end
```

テストがgreenになることを確認。

```irb
yokoyan:~/workspace/sample_app (master) $ rails test
Running via Spring preloader in process 1976
Started with run options --seed 7556

  29/29: [========================================================================================================================] 100% Time: 00:00:01, Time: 00:00:01

Finished in 1.51512s
29 tests, 76 assertions, 0 failures, 0 errors, 0 skips
```

###演習1
*リスト 10.9のテストに１行追加し、正しい数のエラーメッセージが表示されているかテストしてみてましょう。ヒント: 表 5.2で紹介したassert_selectを使ってalertクラスのdivタグを探しだし、「The form contains 4 errors.」というテキストを精査してみましょう。*

画面上は以下の状態になる。

![image](https://qiita-image-store.s3.amazonaws.com/0/98130/b21246a4-6843-f82a-6987-cd33ff647325.png)

エラーの数が4件あるかを確認するために、以下のHTMLが出力されているかをチェックする。

```html
    <div class="alert alert-danger">
      The form contains 4 errors
    </div>
```

テストコードに組み込む。


```/sample_app/test/integration/users_edit_test.rb
  test "unsuccessful edit" do
    get edit_user_path(@user)
    assert_template 'users/edit'
    patch user_path(@user), params: { user: { name: "",
                                              email: "foo@invalid",
                                              password: "foo",
                                              password_confirmation: "bar" } }
    assert_template 'users/edit'
    assert_select "div.alert-danger", "The form contains 4 errors"
  end

```

テスト結果がgreenになることを確認。

```irb
yokoyan:~/workspace/sample_app (master) $ rails test
Running via Spring preloader in process 3205
Started with run options --seed 22571

  29/29: [========================================================================================================================] 100% Time: 00:00:01, Time: 00:00:01

Finished in 1.33817s
29 tests, 78 assertions, 0 failures, 0 errors, 0 skips
```



##10.1.4 TDDで編集を成功させる
###本章での学び
####テストコードの実装
以下のテストをコード化する。

- Fixtureでテストユーザmichealを作成する
- michealのユーザ情報を、GETリクエストで、users#editアクションに送信する
- editビューが描画されること
- 有効な氏名、メールアドレスを入力する（パスワードは空＝変更しない）
- 有効なユーザ情報を、PATCHリクエストで、users#updateアクションに送信する
- flashエラーメッセージが表示されないこと
- michealのプロフィールページにリダイレクトされること
- ユーザー情報を再度DBから読み込む
- 入力した名前と、DBの名前が一致すること
- 入力したメールアドレスと、DBのメールアドレスが一致すること

実装したテストコード。

```/sample_app/test/integration/users_edit_test.rb
  test "successful edit" do
    get edit_user_path(@user)
    assert_template 'users/edit'
    name = "Foo Bar"
    email = "foo@bar.com"
    patch user_path(@user), params: { user: { name: name,
                                              email: email,
                                              password: "",
                                              password_confirmation: "" } }
    assert_not flash.empty?
    assert_redirected_to @user
    @user.reload
    assert_equal name, @user.name
    assert_equal email, @user.email
  end
```

####【Controller】updateアクションの実装

flashメッセージの追加と、ユーザプロフィールページへのリダイレクト処理を追加する。

```
  def update
    @user = User.find(params[:id])
    if @user.update_attributes(user_params)
      # 更新に成功した場合を扱う。
      flash[:success] = "Profile updated"
      redirect_to @user
```

####【Model】パスワードのバリデーション修正
パスワードのバリデーションに`allow_nil: true`を追加することで、
更新時のパスワードが空でも、更新できるようにする。

[railsdoc.com](http://railsdoc.com/validation)によると、
>trueならば、nilの検証はスキップ	

とのこと。

```/sample_app/app/models/user.rb
  validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
```

テスト結果がgreenになることを確認。

```irb
yokoyan:~/workspace/sample_app (master) $ rails test
Running via Spring preloader in process 4806
Started with run options --seed 30426

  30/30: [======================================================================================================================================] 100% Time: 00:00:01, Time: 00:00:01

Finished in 1.33014s
30 tests, 83 assertions, 0 failures, 0 errors, 0 skips
```

###演習1
*実際に編集が成功するかどうか、有効な情報を送信して確かめてみましょう。*

パスワードが空でも更新できることを確認。
![image](https://qiita-image-store.s3.amazonaws.com/0/98130/08815e7d-b074-d37c-890b-1558f453f421.png)

![image](https://qiita-image-store.s3.amazonaws.com/0/98130/f1127716-fd42-6592-3a88-baf322fe58f2.png)

###演習2
*もしGravatarと紐付いていない適当なメールアドレス (foobar@example.comなど) に変更した場合、プロフィール画像はどのように表示されるでしょうか? 実際に編集フォームからメールアドレスを変更して、確認してみてましょう。*

適当なメールアドレスに変更。

![image](https://qiita-image-store.s3.amazonaws.com/0/98130/1e1323a0-3fcd-ffe9-6cdc-879c44834389.png)

プロフィール画像が切り替わることを確認。

![image](https://qiita-image-store.s3.amazonaws.com/0/98130/8d8ca0b6-797c-21f8-7ea3-5c56cda12801.png)

#おわりに
9章に比べるとずいぶん楽に感じます。
railsはCRUD処理を書くのが本当に楽ですね。
これまでrailsコンソールでの動作確認以外では、1度もSQLを書いていません。
パーシャルでのリファクタリングも新鮮でした。
rails書いてて楽しいなあ。仕事で使いたくなってきたよほんとに。。。