Railsで共通の処理をどのようにまとめるか、考えてみた

# 記事を書くにあたって
Railsで実装を行なっている際に同じ処理が複数の箇所に記載されてしまうため、モヤモヤして色々と調べた結果です。他にもこういったものがあるよ！　とコメント欄で教えていただけると嬉しいです！　ポリモーフィック関連付と単一テーブル継承に関しては私自身あまり理解していないので、今回の項目からは除外しています。（個人的にはメリットよりデメリットを大きく感じる……）

# Railsで共通の処理はどう実装するの？
concern、merge、extendingを紹介いたします！　これらを使用すれば、共通の処理をまとめることができるのではないかと考えています！

# concern、merge、extendingはそれぞれ何を共通化できるの？

|  機能  |  共通化できるもの  |
| ---- | ---- |
| concern  | モデルのクラスやインスタンスに対しての処理 |
| merge  |  scope(他のモデルのscopeを呼び出すことが可能になる。) |
| extending  | モデルの集合のインスタンスに対しての処理 |

# 説明の元となるモデルに関して

|  モデル名称（JP）  |  列（JP）  |
| ---- | ---- |
| MissionCar（MT車）  |  name（名前）, manufacturer_id（メーカーID）,　car_model(車種)|
| AutomaticCar(AT車)  |  name（名前）, manufacturer_id（メーカーID）,　car_model(車種)|


|  モデル名称（JP）  |  列（JP）  |
| ---- | ---- |
| Manufacturer(メーカー)  | name（名前） |


## concernに関して
### 共通で書かない場合
例えば、Enumerizeを使用する場合に複数のモデルに同じ処理を書いたことはありませんか？
下記の例のような形です。

この場合、car_modelに新しく種類を追加して欲しい！　と言われた時にMissionCar(MT車)とAutomaticCar（AT車）の両方を修正しないといけませんよね？

```app/models/automatic_car.rb
class AutomaticCar < ApplicationRecord
  belongs_to :manufacturer
  extend Enumerize

  enumerize :car_model, in: %i[sedan suv minivan open] scope: true

  scope :familiesa_car , lambda{ where(car_model: %w[sedan suv minivan]) }
end
```

```app/models/mission_car.rb
class MissionCar < ApplicationRecord
  belongs_to :manufacturer
  extend Enumerize

  enumerize :car_model, in: %i[sedan suv minivan open] scope: true

  scope :familiesa_car , lambda{ where(car_model: %w[sedan suv minivan]) }
end
```

### 共通で書いた場合

Concernを使用して共通の処理を抜き出してみます。

下記のようにすることでenumerizeを一つのmoduleに閉じ込めることができます。
今後、car_modelに追加や削除があってもCarModelに対して修正を行い、MissionCar(MT車)やAutomaticCar（AT車）に修正する必要は無くなりました。

```app/models/concerns/car_model.rb
module CarModel
  extend Enumerize
  extend ActiveSupport::Concern

  enumerize :car_model, in: %i[sedan suv minivan open] scope: true

  included do
    scope :familiesa_car , lambda{ where(car_model: %w[sedan suv minivan]) }
  end
end
```

```app/models/automatic_car.rb
class AutomaticCar < ApplicationRecord
  include CarModel
  belongs_to :manufacturer
end
```

```app/models/mission_car.rb
class MissionCar < ApplicationRecord
  include CarModel
  belongs_to :manufacturer
end
```

## Mergeに関して
### 共通で書かない場合

例えば、MissionCar(MT車)やAutomaticCar（AT車）がManufacturer(メーカー)のname（名前）に関して検索を行いたいとします。
MissionCar(MT車)やAutomaticCar（AT車）にそれぞれ下記のように書いていませんか？

もし、システムの使用が部分一致ではなく完全一致になった場合対象の処理を全て修正しないといけませんよね？　

```app/models/automatic_car.rb
class AutomaticCar < ApplicationRecord
  include CarModel
  belongs_to :manufacturer

　　scope :where_manufacturer_name, -> (search_name) { eager_load(:manufacturer).where('name LIKE ?', "%{ search_name }%") if name.present? }
end
```

```app/models/mission_car.rb
class MissionCar < ApplicationRecord
  include CarModel
  belongs_to :manufacturer

　　scope :where_manufacturer_name, -> (search_name) { eager_load(:manufacturer).where('name LIKE ?', "%{ search_name }%") if name.present? }
end
```

### 共通で書いた場合

Mergeを使用することで、Mergeで指定したクラスのscopeを使用することが可能になります。
同じscopeを複数の箇所に持たせたいのであれば、Concernを使用すれば良いと思います。

``` app/models/manufacturer.rb
class Manufacturer < ApplicationRecord
  has_many :mission_cars
  has_many :automatic_cars

　　 scope :where_name, -> (search_name) { where('name LIKE ?', "%{ search_name }%") if name.present? }

end

```

```app/models/automatic_car.rb
class AutomaticCar < ApplicationRecord
  include CarModel
  belongs_to :manufacturer

 # 或いはscopeで直接merge(Manufacturer.where_name(search_name)と呼び出しても良い
  scope :where_manufacturer_name, -> (search_name) { eager_load(:manufacturer).merge(Manufacturer.where_name(search_name)) }
end
```

```app/models/mission_car.rb
class MissionCar < ApplicationRecord
  include CarModel
  belongs_to :manufacturer

 # 或いはscopeで直接merge(Manufacturer.where_name(search_name)と呼び出しても良い
　　scope :where_manufacturer_name, -> (search_name) { eager_load(:manufacturer).merge(Manufacturer.where_name(search_name)) }
end
```

## Extendingに関して

### 共通で書かない場合

この機能が一番知りたかった機能でした！
例えば、Paginationの処理ですが複数のコントローラーに何度もpage(params[:page]).per(20)と貼り付けていますよね？　２０件が３０件になった時やページネーションのGEMを変更する時を考えると頭が痛くなります。

```app/controllers/manufacturers_controller.rb
  def index
    @manufacturers = Manufacturer.all.params[:page]).per(20)
  end
```

### 共通で書かいた場合
Railsのextendingを使用することで一つのファイルにまとめることができました！　これで変更する際にも修正箇所はPaginationモジュール一つのみになります。

```app/extensions/paginate.rb
# 実際にこのファイルを作成した場合はRailsを再起動してください。
# 再起動しない場合はファイルをRailsが読み込みません。
module Pagination
  def paginate(page)
    page(page).per(10)
  end
end
```

```app/controllers/manufacturers_controller.rb
  def index
    @manufacturers = Manufacturer.all.extending(Pagination)
    @manufacturers.paginate(params[:page])
  end
```

## 参考
[Railsガイド](https://railsdoc.com/page/extending)