Ruby on Rails チュートリアル集中講座３日目

日本語版Rails Tutorialhttps://railstutorial.jp/
を運営しているYassLabさんが講師！
たった５日間であの長いRails Tutorialを完走する集中講座に参加しています。
https://coedo-dev.doorkeeper.jp/events/72968

なんかメモの質がどんどん走り書きになってるけど、
メモだからQiita投稿しちゃいます。


#第7章 ユーザー登録〜推測可能であることの素晴らしさ〜

##RESTful
Railsの設計のひとつ。
APIまわりを綺麗にしよう。リファレンス読まなくても **推測** できるのがRESTの嬉しいところ。

usersまわりの７つのリクエスト。scaffoldお得パック。

|リク|URL|名前付きルート|コントローラアクション|対応するモデルの動作|
|---|---|---|---|---|
|GET  |/users       |user_path      |index|User.all|
|GET  |/users/new   |user_path(user)|new|User.new(...)|
|POST |/users       |new_user_path  |create|User.create(...)|
|GET  |/users/1     |user_path     |show|User.find(1)|
|GET  |/users/1/edit|edit_user_path(user)|edit|User.find(1)|
|PATCH|/users/1     |user_path(user)|update|User.find(1).update_attributes|
|DELETE|/users/1    |user_path(user)|destroy|User.find(1).destroy|

/users/new というより/signup  の方が簡単だよね。

DELETE専用のページは普通無い（Showとかでいい）
ユーザー退会ページとかは用意してもいい

PUTリクエストとPATCHリクエストの違い
どうも過去のものになっちゃったらしい。
PUTはそもそも一括更新って意味だった。（同じことできるので残ってはいる。




###Q.TwitterはRESTじゃない？
<https://twitter.com/kugyu10>

/user/kugyu10 だったら分かるんだけど、
/kugyu10  になってる。これはRestなのか？

A.RESTかどうかは本質的ではない。推測できればいいんじゃない？

RESTパターンにあわせると推測しやすいけど、
RESTパターンじゃなくても推測できればいいんじゃない？という話。

QiitaのURLの例（/ユーザー名/items/アイテムID）
<https://qiita.com/kugyu10/items/b743dc3d50c145dd85ef>

多分、中ではUser.find_by(name:"kugyu10").items.find_by(id:...) みたいなことしてる？

GitHubのURLの例。（/人/リポジトリ って感じ？）
<https://github.com/kugyu10/flow_statter>

たしかに、推測はできる。

##SampleAppでユーザーを表示させよう。



###Debuggerの出番

debug(params) if Rails.env.development?
後置if便利。一行ですむ喜び。


###Routes.rbを設定しよう

```routes.rb
resources :user
```

routesが増えてきたら、 

```
rails routes
```

で一覧が確認できる
PUTはPATCHの代わりになる。

###params
ハッシュみたいに使える
今回はparams[:id]で GETからきたURL /users/1 の1が取得できる。

###インスタンス変数って？

ふつーに `user` って書いたらローカル変数。
メソッドの中でしか生きられない。
view側で見るためには、view側で宣言するか、↓で作ってもらう

リクエスト内？

@user 
view側でも見れるようになる。

@@user
$user

とかいうクラス変数、グローバル変数もある。
基本、使わない方が幸せ。

（グローバル変数がある、ということは、
あった方が嬉しいという特殊なケースがあるのだろう。
ただ、初心者のうちは忘れた方が良さそうだ）

###ブレークポイント
byebugコマンド

理想的には使わない方が基本はデバッグは速いので多様しないこと。
現に、初期のRubyには無かった。

もちろん、こみいっているバグや訳がわからないとき便利。


###Gravoterを使う
これはUsersHelperに
`gravatar_for(user)` を定義する。

###form_for
User.newで、空のUser（雛形）をインスタンス変数にいれて
new.html.erbにわたす


##7つのアクション
index, show, new, create, edit, update, destroy

この順番で書くのが慣習。
abc順じゃないし、なんの順番だろう？


##form_for
じつはたくさん機能がある。Railsガイド参照。

###email_field
スマホで@や.が打ちやすくなる。

###password_field
***で隠してくれる。
（あとなんかOnePassとかが反応してくれる。）

## Create my accountの作り方
もう、POSTで送るところまでできてる。

POSTを送ったあと、
６章で作ったバリデーションを試して
DBにsaveにした。

### CSRFを防ぐトークン 
（application.html.erbに入ってる）

###form_with って？

Rails5.1から、form_forとform_tagを統合しようとして実装されたもの。
そもそもform_forとform_tagって同じようなことをやってるよね？って話で。
使いやすいなら今後そっちに統一されるかも？

###Ajaxで
一応、ソレ用にform作るオプションがある。remote:true

## 余談


###Railsのコントリビューターはたくさんいるが、
Rails開発してるコアチームは世界で１２人程度。

日本人もコミットしてる人は３人いる。

###Kamipoさんはすごい人
ぐぐったらほんとに凄い人だった件。

Yaginumaさんのブログ。Railsの最新動向がでてる。情報収集の習慣づけとしておすすめ。
amatsudaさん

##createアクション
###@user = User.new(params[:user])だけでいい理由

params[:user]が入れ子構造になってて
user_idもemailも入っているから。

### nilとfalse以外はtrueになると嬉しい理由

```
if @user.save
  # 成功
else
  # 失敗
end
```

みたいな短い書き方できる。

（たとえばJavaだったら、nullかもしれないものは
if (hogehoge == null) {} と書くしかない。
（Nullableがいまいち使えない））


###ForbiddenAttributesError

文法的にはソレでいいんだけど、
（form_for経由の場合は脆弱性生むので）禁止していますよ、
ってエラー。

こっそりparam[:user][:admin]=trueとか混ぜて
送信してくるかもしれないので対策してね。って意味。

こういうのを防ぐストロングパラメータにすれば解決。

```:user_controller.rb
#user_paramsは以下4つの属性しか許可しませんよ
private
  def user_params
    params.require(:user).permit(:name, :email, :password,
      :password_confirmation)
  end

```

###error_massagesパーシャル
sharedに入れたのは、今後、フォームがあるたんびに作るから。

###pluralize
べんり

```
'child'.pluralize # "children"

'men'.singularize # "man"
```

# GET /users/:id 

```
redirect_to user_path(@user.id) #平たく書くとこう
redirect_to user_path(@user) #.idは省略できる
redirect_to @user #さらにここまで省略できる
```


###flash

ハッシュっぽく使える

```
flash[:success] = 'Welcome to Sample App!'
```

keyをdangerとかsuccessとかにすると
そのままbootstrapのクラス指定に使えるよね、って小技。
（そのまま、といいつつviewでは "alert-"を前に足してあげないといけないのだが）

<http://bootstrap3.cyberlab.info/components/alerts.html>

|flashのkey|bootstrapで使えるクラス名|色|
|---|---|---|
|:success|alert alert-success|緑|
|:info|alert alert-info|青|
|:warning|alert alert-warning|黄色|
|:danger|alert alert-danger|赤|


###SSL,Puma,Procfileの設定
設定ぜんぜんわからん。今回はコピペる。



#８章 基本的なログイン機構 〜ここからが本当の地獄だ〜

ログイン・セッション周り。
普通はMVCでやるんだけど、
Sessionはモデル・DBで扱うとかえって面倒くさい。

かわりにcookie（クライアント側のブラウザ）と
Sessionsリソース（サーバーとブラウザ間の情報）を使う

モデルが無いから頑張らないとね、みたいなことも多い。


###Sessionのルーティング


GET /login ログインページを表示する
POST /login ログインボタン押した！
DELETE /logout ログアウト


```routes.rb

```

###さっそく、Controllerを作ろう

今まで
def new
  @user = User.new
end

あれ？今回モデル作ってないぞ。


今回やりたいことは、ログインページで受け取ったものを
POST /login に渡してあげればいい。
Routesで設定すれば、Sessions#createに渡るので。

で、今回の書き方。sessionっていうシンボルを渡す。

form_for(:session, url: login_path)



###クラスメソッドの話
def User.all
end

###authenticate

has_secure_passwordするとできるメソッド。

nilで呼び出そうとすると死ぬ。当然か。
なのでnilガードが必要。Rubyならnilガードも一行。

#トリッキーな書き方

```
if user && user.authenticate(...)
```

user.authenticateは見つかったらオブジェクトかfalseを返す。
find_byでもなんでも、見つかったらオブジェクトを返すことがRubyは多い。
なぜならオブジェクトがあればなんでもtrue、nilはfalseと評価できるで。

booleanを返してほしいメソッドなら、
authenticate? みたいに「?」を末尾に書くのが慣習。

OSSでもすごい良い機能だけど、名前がダメだからマージされなかった、
とかめっちゃあるあるらしい。Java以上にRubyでは名前はすごい大事。

###redirect_to と renderの違い

flash 次の１回目のリクエストが来るまで保持し続ける。
redirectなら、それもリクエストなので、redirectした瞬間、
（flashを最後読み取って表示してから）消える

renderは描画なので、同じリクエスト内の処理の処理。
なのでまだ、次のリクエスト時にも残ってる

直すのは簡単。flash.now にするだけ。

renderする時はflash.now
redirect_toする時はflashを使う、って覚え方で多分大丈夫。

###loginしてるかどうか

SessionsHelperに書く

session
- name
- password
- hoge

session.hogeがnilでないならOK


で、ログインの機能はありとあらゆる操作で欲しいので
ApplicationControllerにincludeする。
これでSampleApp内でどこでも使える。


###復元

sessionの状態を見るメソッドとしてcurrent_userメソッド作る。


##メモ化
パフォーマンスがよくなる

基本的にfindはDB問い合わせ。

current_userメソッドたたくたびに、毎回
DB問い合わせしたら大変なので、
一度インスタンス変数にメモ化できるとうれしい。

```
#メモ化を平たく書くとこうなる
if @current_user.nil?
  @current_user = User.find_by(id: session[:user_id])
else
  @current_user
end
```

Rubyは、@current_userの真偽は（nil以外なら）常にtrue。
上記のトリッキーな書き方同様、こうも書ける

```
@current_user = @current_user || User.find_by(...)
```

さらにこう短く書くのがRails流。

```
@current_user ||= User.find_by(...)
```

（未だに見ると一瞬びびるので
呼吸するように書きたい。）

###logged_in?

?で終わってるので、booleanで返るのが明らか。

logout
method delete
method patch


###統合テスト用のデータを作る、ための下ごしらえ。
DBには生パスワードじゃなくて、ハッシュ化したものいれないといけないから。
fixtureの用意もなかなか大変。

で、ハッシュ化させるクラスメソッドを用意する。
User.digest(string)
中身はざっくりの理解でOK

###三項演算子 ？と：

```
A ? B : C
```

A がtrueならB実行、そうでないならC実行。
?はメソッド名で良く使うし、：も::とかしたりするから
慣れるまでややこしそう。

###ファットコントローラー問題
ありがち
cookpadくらいになるとファットモデル問題もあるらしい。





#９章 発展的なログイン機構〜Chromeさんが頭良くなりすぎた件〜


タブを閉じても大丈夫なようにする機能。
Chromeさんはある程度の期間、sessionを保ってくれるので重要度が下がったかも。

9章の機能をいれれば、ブラウザどころかMacOSをリブートしても大丈夫。
ユーザー体験向上！

ただし、セッションハイジャックのリスクと常に背中合わせ。

##セッションの寿命

- 銀行の振込とか決済サービスとかなら、
セッションハイジャックのリスクは絶対いや。
数分でセッションを切っていく。

- 機密とかを扱うサービス
タブを閉じたら期限切れに。

- SNSなどのサービス
  セッションを永続化しているサービスが多い。

で、cookieを使う
cookieはクライアントのブラウザの中に保存されるテキストデータ。

cookieの中にRemember meトークンが保存されている。

ハッシュ関数を通してダイジェストでやりとりする。
DBに保存するのもダイジェスト。

記憶トークンはパスワード同様、なるべく触らない、覚えない、
漏らさないように気をつける。

##今回の実装概要図を見ながら説明

署名付（暗号化済みの）ユーザーID

signed[:user_id]340394→本物のuser_id:3

トークンはユーザー名と違ってユーザーが書かない選ばない。
Railsがランダムの文字列を作って勝手にブラウザのクッキーに置く。
そして、ハッシュ化したダイジェストだけDB保存する。



##いざ実装

Userにremember_digestのカラムを追加しよう

```
rails generate migration add_remember_digest_to_users remember_digest:string
rails db:migrate
```

_ばかりなのにちゃんと理解できるRails。

###has_secure_passwordがやってくれたことをやる

password（フォーム用）
password_confirmation （フォーム用）
password_digest (これだけDBに保存する）

###メソッドの最後がイコールなメソッド
仮想的なカラム（一時的にインスタンス変数にいれるけどDBには保存しない）
としてふるまってくれる。ここまでRuby。

で、アトリビュートアクセッサ(sttr_accessor) というRailsが敷かれている。
よくあるメソッドとして、書ける。



##クラス・メソッドの定義

```
User.digest()

#ここでのselfはUserになる
self.digest()
```

メタプロするとき、selfじゃないとダメな時がある。
（メタプロしないならUserでいいかな・・・？）

##cookiesを永続化する

```
#これはcookiesのexpires:を 20.years.from_now.utc にするのと同じ。
cookies.permanent
```

##ユーザーIDを署名してcookieにおく

cookies.signed[:user_id] = user.id でおk

つまりメソッドチェーンすれば

```
cookies.permanent.signed[:user_id] = user.id
```

###user_idの探し方

1.sessionにあればそれ使う

2.cookiesにあればそれ使う

3.どっちも無いならだめ


if (user_id = cookies.signed[:user_id])

Javaだと9割方比較演算子==のたいぽだけど、Rubyならこれもよくある記法。
条件式の中で代入している。nilならfalseになるので。



###２つの目立たないバグ

・連続ログアウト問題
再現できればあとは簡単。

・複数種類のブラウザを使った問題。ほんと目立たない（笑）

tokenがあって、digestだけnil、という特殊な状況でエラー発生。
・別のブラウザで同時にログインする（別々にcookie保存）
・片方のブラウザを閉じて開く（nilになっちゃう）

単体テストと結合テストでログインの方法が違うけど、
同じlog_in_asメソッド名で実装して、
似たように使えるようにしてしまう。

log_in(user)
logged_in?
is_logged_in?
log_in_as(user)
log_in_as(user)


さすがにややこしくない？
何がうれしいか？
一番は、エンジニアにとって推測しやすい、ということ。
推測ができて、そのとおりにできるなら、中身は意識しなくていい。

どこまでダックタイピングするかは、
ガイドラインとかスタイルとかチームの合意次第でもある。


---

#疑問点
- @current_userのインスタンス変数の寿命は？
- sessionまわりが全体的にもやっとする


#TODO

- 9章まだ完了してないので実装する。
- 分からないの承知で、Railsガイド（コントローラーとかセッションとか）に目を通す。
- なんかBitBucketができてないので直す。