Railsチュートリアルメモ - 第13章

[メモの目次記事はこちら](https://qiita.com/yokohama4580/items/dedfd5510080273dc2a0)

[公式Railsチュートリアル第13章へのリンク](https://railstutorial.jp/chapters/user_microposts?version=5.1#cha-user_microposts)

## サマリ
- マイクロソフトの表示、投稿、削除
  - モデルの関連付け（`has_many`および`belongs_to`）
  - モデルを使ったレコードの取得（取得条件、並び順、取得件数の指定）
  - ラムダ式 (Stabby lambda) を使ったProcオブジェクトの生成
- Homeページの動的な出し分け
- CarrierWaveを使った画像ファイルのアップロード
- ImageMagick(+MiniMagick)を使った画像ファイルのリサイズ
- 本番環境でのfogを使ったS3への画像アップロード

## ポイント
- `rails generate model`の際、`references `を指定するとFKの指定を行ってくれる

e.g. `rails generate model Micropost content:text user:references`を実行した場合

```ruby
class Micropost < ApplicationRecord
   belongs_to :user
end
```

- モデルの関連付けを行うと、1対Nの1側からN側のオブジェクトを生成することができ、N側のオブジェクトには1側のオブジェクトのidが設定される。
    - e.g. 
        - `user.microposts.create`
        - `user.microposts.create!` => !をつけると生成失敗時に例外を発生させる
        - `user.microposts.build`
            - `build`メソッドはオブジェクトを返すがデータベースに反映はしない

- モデル内で`default_scope`を指定することで、並び順を指定することができる

```ruby
default_scope -> { order(created_at: :desc) }
```
- ->というラムダ式は、ブロックを引数に取り、Procオブジェクトを返す
- Procオブジェクトは、callメソッドが呼ばれたとき、ブロック内の処理を評価する
- モデルの関連付けをした1対多の1側に`dependent: :destroy`を付与しておくと、1側のオブジェクトが削除された際に、多側のオブジェクトも一緒に削除される
- orderメソッド、takeメソッドを使って取得するレコードの並び順と件数を指定できる

```ruby
User.order(:created_at).take(6)
```

- whereメソッドでレコードの取得条件を指定できる。?句を使うことで、変数をエスケープし、SQLインジェクションを防ぐことができる。

```ruby
  def feed
    Micropost.where("user_id = ?", id)
  end
```

- フォーム内でバリデーションエラーがあった場合、form_forのブロック変数のobject属性にエラーが入る
- errorパーシャルなどにrenderから変数を引き渡す場合、第二引数にハッシュを指定する
e.g.

```erb
  <%= form_for(@user) do |f| %>
  <%= render 'shared/error_messages', object: f.object %>
  <% end %>
```

```erb
<% if object.errors.any? %>
<div id="error_explanation">
    <ul>
        <% object.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
        <% end %>
    </ul>
</div>
<% end %>
```

renderには以下の使い方がある

1. 引数ががアクション名（new, editなど） => アクションに対応したerbの描画
2. 引数がパーシャル => パーシャルの描画（第二引数に与えた変数をパーシャル内で使用できる）
3. 引数がインスタンス変数 => インスタンス変数に対応したパーシャルを探して描画
    - e.g. `render @feed_items`で@feed_itemsの中身がmicropostであれば_micropostパーシャルを探して描画

- フォームが含まれる画面を描画する際は、form_for(@インスタンス変数)の@インスタンス変数をbuildして空のオブジェクトを用意してから描画する必要がある。
- フォームが送信されたら改めてparamsから取得した値を使用して@インスタンス変数をbuildしsaveする。
- erb内の`link_to`メソッドに`data: { confirm: "You sure?" }`を渡すとリンククリック時に確認のダイアログが表示される
- `redirect_to request.referrer`と記載すると、一つ前のページにリダイレクトさせられる

- CarrierWaveを使った画像ファイルの取り扱い手順
    - Gemfileに`carrierwave`を追加し、bundle install
    - `rails generate uploader Xxx`でアップローダーを作成
    - モデルに`mount_uploader :picture, PictureUploader`を追加
    - ビューにimage_tagを追加
        - `<%= image_tag micropost.picture.url if micropost.picture? %>`

```erb
<span class="picture">
    <%= f.file_field :picture %>
</span>
```

- モデルに独自定義したバリデーションを追加する場合は、`validates`ではなく`validate`を使用する

## 感想
- 終盤のコンテンツだけあってなかなか内容が盛りだくさんだった。
- リスト13.50で空の配列（@feed_items = []）を追加している箇所は、`_feed.html.erb`の方を以下のように変えた方がわかりやすい気がした。

```ruby
<% if @feed_items && @feed_items.any? %>
```

- ただ、これだと空の配列追加と同様、フィードの一覧が表示されないので、普通に`redirect_to root_url`でHomeにリダイレクトすればよいのではと思ってやってみたが、リダイレクトするとflashメッセージが表示されずエラーが発生したことがわからない。やはり`render 'static_pages/home'`は必要なよう。

- uploader導入後のローカルテストがredになり以下のERRORが表示された。
    - NameError: uninitialized constant Micropost::PictureUploader
            app/models/micropost.rb:4:in `<class:Micropost>'
            app/models/micropost.rb:1:in `<top (required)>'
- サーバーを再起動しても解消せず原因が分からなかったが、一度VSCodeのすべてのターミナルを閉じてから再度開き直して実行したところgreenになった。

- herokuにpushした際、以下のエラーが発生した。

```bash
remote:  !
remote:  !     Failed to install gems via Bundler.
remote:  !
（省略）
To https://git.heroku.com/xxx.git
 ! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to 'https://git.heroku.com/xxx.git'
```

- herokuはpushされた際にbundle installも実行するようで、そこでこけるとpushがエラーになる模様（たしかにここまで一度もherokuでbundle installをしなかったが、勝手にやってくれていたらしい）
- bundle installがこけた原因は`heroku config:set`でAWSクレデンシャルを環境変数に設定していなかったことが原因のようで設定してからpushすると正常に動作した。