Rails開発で最低限気をつけること(=気をつけてほしいこと)

## 概要

Rails開発で最低限気をつけること(=気をつけてほしいこと)をメモ
少し長いですがRailsを覚え始めの人に読んでいただければと。

## 全般

### タイポをしない

当たり前ですが、タイポの原因はタイピングをミスすることです
残念ながらrubyは動的型付け言語であり、タイポによるエラーはプログラム実行時に発生します
タイポを防ぐ方法として、

- Lintを活用する
- 変数はコピペする
- スペルが間違っていないか辞書 or Google で検索するようにする

などが挙げられます
上記を徹底し、 **視覚的にタイポを防ぐ** 、 **タイピングをやめる**  ことでタイポ0を目指しましょう

### インデントを揃える

PullRequestを作成する前には、必ずコードのフォーマットを直しましょう
インデントが揃っていることはソフトウェアエンジニアのマナーの一つだと思います

### String vs Symbol

Symbolの方がパフォーマンスが良いため、できるだけStringではなくSymbolを使うようにしてください
理由は下記の記事を参考ください
[Rubyの文字列とシンボルの違いをキッチリ説明できる人になりたい - Qiita](https://qiita.com/Kta-M/items/53a13ef60e14fcb41193)

### `if` vs `if not` vs `unless`

rubyでは条件式の判定に、 `if` `if not` `unless` などがあります
条件式が複雑かつ、否定演算子などが組み合わされると一気に可読性が低下し、バグの原因につながるため、
条件式では「常に正常系を」書くようにしています
いくつか例を挙げてみます

例: `if ~ else` 構文

```ruby
if @record.save # 条件式は正常系
  # 成功時の処理を書く
else
  # 失敗時の処理を書く
end
```

例 `unless` でGurad構文(=早期リターン)

```ruby
def process(record)
  return false unless record.valid? # 早期リターン(=条件式は正常系)
  # 以下、正常系の処理を実行
end
```

「常に正常系を」を記述することのを推奨する理由については下記リンクを参考にしてみてください
[ifとunlessの使い分け](https://qiita.com/raamen/items/7dc9f7b89e76cc81866b)

### boolean型のメソッド名には末尾に `?` をつける

一般的に、rubyではbooleanを返すメソッドには末尾に `?` をつける習慣があります
メソッド名の末尾に `?` がついていることで boolean を返すことがわかり可読性が上がります

### ぼっち演算子 `&.` をつける

rubyでは、2.3から `&.` 演算子（safe navigation operator = ぼっち演算子）が導入されました
実行時に nil なオブジェクトに対してメソッドチェーンを実行すると `undifined method error for nil:NilClass` と
なってしまうため、**`nil` になる可能性のあるメソッドチェーン呼び出しには全て `&.` をつけるようにすべきです**

### ソースコードの責任の範囲を意識する

OOPの思想に沿って、ソースコードの責任の範囲を考えて設計＆実装する
Rails開発はMVCに依存しがちになますが、もっと柔軟にクラスを切った方が良いと思います
また、アプリケーションやビジネスロジックに依存せず汎用的に使えるクラスは `lib` 配下に記述することもおすすめします

例として、下記のようにディレクトリを切ると良いかなと思います

```
- app
  - controllers
  - decorators
  - forms # form経由の値を扱う(Controllerをファットにさせない)
  - helpers
  - jobs
  - listeners # wisperなど、Observerパターンでcallbackを処理する
  - mailers
  - models
  - operators # ビジネスロジックを扱う(ControllerやModelをファットにさせない)
  - policies # punditなどで権限管理
  - validators # 独自定義したバリデーション
  - values # Valueオブジェクト
- lib
  - xxx
  - xxx
  - xxx
```

## Controller

### `before_action` でインスタンス変数への代入を控える

`before_action` を複数記述した場合、記述した順番に実行されます
`before_action` 内で、他の `before_action` で代入された変数を呼び出すと、
プログラムが正しく動作するかどうかが `before_action` を呼び出す順番に依存してしまい、
スパゲッティコードにつながってしまいます
可能であれば変数を取得できるメソッドを作成することをおすすめします

× `before_action` を使う

```ruby
class UsersController < ActionController::Base
  before_action :set_user, only: [:show]
  def show
  end
  def set_user
    @user = User.find(params[:id])
  end
end
```

◯ 専用のメソッドを用意する

```ruby
class UsersController < ActionController::Base
  def show
  end
  def user
    @user = User.find(params[:id])
  end
  helper_method: user
end
```

`helper_method :user`  とすることで、viewファイルでも変数を参照できるようになります

### インスタンス変数をメモ化する

`ActiveRecord` 経由で取得するデータは、変数をメモ化することで値のキャッシュができます
展開された変数はリクエスト終了時にクリアされるため、可能な限りキャッシュすることをおすすめします

```ruby
class UsersController < ActionController::Base
  def show
  end
  def user
    # @note @user がnilの場合のみ代入され、nilでなければキャッシュされたデータを返す
    @user ||= User.find(params[:id])
  end
  helper_method: user
end
```

### レコードの保存処理を行う場合は `flash` を表示する

レコードの保存処理を行う場合は成功時&失敗時に合わせて適切なflashメッセージを表示するようにしてください

```ruby
def create
  if @record.save
    redirect_to records_path
    flash[:success] = 'レコードの保存に成功しました'
  else
    render :new
    flash.now[:error] = 'レコードの保存に失敗しました'
  end
end

def destroy
  if @record.destroy
    redirect_to records_path, flash: { success: 'レコードを削除しました' }
  else
    redirect_to records_path, flash: { error: 'レコードを削除できませんでした' }
  end
end
```

また、レコードの更新に失敗し `render` を実行する場合は `flash.now` を使うようにしてください
参考: [【Rails】flashとflash.nowの違い - avosalmonのブログ](http://avosalmon.hatenablog.com/entry/2014/05/17/%E3%80%90Rails%E3%80%91flash%E3%81%A8flash.now%E3%81%AE%E9%81%95%E3%81%84)

## Model

### `belongs_to` には `required: true` または `presence: true` をつける

`belongs_to` で必ず親となるレコードが存在する場合は、 `required: true` または `presence: true` を記述してください

### `has_one`, `has_many` には `class_name` をつける

 `has_one` または `has_many` を使う場合は、 `class_name` オプションをつけることで、
モデルが取得できずエラーとなってしまうことを防ぐことができる場合があるため、記述するようにするべきです
必要に応じて `foreign_key` も指定することをおすすめします

### `has_one`, `has_many` には `dependent` オプションをつける

削除時に `has_one` または `has_many` 先の子モデルに対する振る舞いを `dependent` オプションで定義できます
不要なレコードはDBのパフォーマンスにも影響が出るので、適切に設定することをおすすめします
`dependent` オプションについては、下記リンクで詳しく説明されていたので参照してみてください

[ActiveRecord::has_manyのdependentオプション](http://blog.sanojimaru.com/post/18426241191/activerecordhasmany%E3%81%AEdependent%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3)

### バリデーションを記述する

DB層でのバリデーションが必要なカラムに対しては、`validates` メソッド等を使って
適宜バリデーションを記述する
また、必要に応じて適宜カスタムバリデーションを作成して再利用できるようにすることもおすすめします
[カスタムバリデーションを実行する](https://railsguides.jp/active_record_validations.html#%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%A0%E3%83%90%E3%83%AA%E3%83%87%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B)

## View

### `form_with` を使う

Rails5.1からは `form_for` と `form_tag` が `form_with` というインターフェースに統合され、
URLベース、スコープ、モデルを指定してformタグを生成できるようになりました

[Ruby on Rails 5.1リリースノート](https://railsguides.jp/5_1_release_notes.html#form-for%E3%81%A8form-tag%E3%81%AEform-with%E3%81%B8%E3%81%AE%E7%B5%B1%E5%90%88)

※ `form_for` を使う場合は `local: true` を設定してください
https://qiita.com/bluegirl_beer/items/a361171f653edcd888ad

### `form_for` や `form_with` ではモデルの指定でaction先を絞る

railsで自動生成されるpathって読みづらいですよね
`form_for` や `form_with` では、渡すオブジェクトが保存されているかどうかで
`create` のアクションを実行するか、 `update` のアクションを実行するかを自動的に判別してくれます
また、必要に応じて `method` や `action` オプションを渡すことで、アクション先を絞ることができます


## DB

### migrationファイルでカラムを追加するときは `comment` をつける

`comment` オプションをつけることで、生成されるスキーマファイルにコメントを追加できます
可能な限りコメントの追加をお願いします

```ruby
class CreateBlogs < ActiveRecord::Migration[5.1]
  def change
    create_table :blogs, comment: 'ブログ' do |t|
      t.string :title, comment: 'タイトル'
      t.text :body, comment: '本文'
      t.timestamps
      t.datetime :deleted_at
    end
  end
end
```