【Ruby on Rails】concerns,helpers,decorators の使い方について考えてみた

# **はじめに**

皆様初めまして。プログラミングスクールで現在 Ruby on Rails を勉強しております、うーさんと申します。

2023 年 4 月よりプログラマーへの門を叩いてから早半年の月日が経ちました。

# 概要

`Ruby on Rails` には MVC 機能において、Model や Controller のコードの重複や肥大化(「`Fat Model`」,「`Fat Controller`」)を防ぐために、共通的なロジックを部分ごとにモジュールやオブジェクトとして分離する役割を果たす `concerns`, `helpers`,`decorators` といったファイルや概念が存在します。

とは言っても、結局それって何を書くの？どのファイルにどのようなメソッドを書けばいいのか？と疑問に思いましたので、深掘りも兼ねてこの度初めて技術記事にさせていただきました。
<br>

::: note warn
💡 誤った知識を記載してしまう場合がございます。
至らぬ点等何かございましたら、お手数ですがご教示いただけますと幸いです。
:::



# concerns

- Rails アプリケーション内で同じロジックを複数の場所で再利用するための仕組み。これにより、コードの整理、管理、保守が容易になる。
- `ActiveSupport::Concern` は、Rails が提供するモジュールの拡張機能で、モジュールをより簡潔に使うためのもの。

## Controller Concerns

Controller で使う特定の機能やロジック（例: ログインチェック、特定の設定の読み込みなど）を別のモジュールとして分離し、複数の Controller 間でその機能やロジックを共有できる。

同じロジックを複数の Controller で繰り返し書くのではなく、一箇所にまとめて管理し、必要な Controller で簡単にそのロジックを再利用することができる。

例:
複数の Controller で使用する `set_params` のようなメソッドを `concern`として定義。


```ruby:app/controllers/concerns/param_set.rb

module ParamSet
  extend ActiveSupport::Concern
#extendを使用することで、ActiveSupport::Concernのメソッドをクラスメソッドとして追加。

  private

  def set_params
    params.require(:model_name).permit(:attribute1, :attribute2, ...)
  end
end

```

複数の Controller でこのモジュールをインクルードすることで、そのロジックを再利用できる。

```ruby: app/controller

class SomeController < ApplicationController
  include ParamSet
  end
```

## Model Concerns

Model で使う特定の機能やビジネスロジック（例: 共通のバリデーション、スコープ、メソッドなど）を別のモジュールとして分離し、複数の Model 間でその機能やロジックを共有するためのもの。
同じロジックを複数の Model で繰り返し書くのではなく、一箇所にまとめて管理し、必要な Model で簡単にそのロジックを再利用することができる。

（ビジネスロジック…アプリケーションの核心的な機能や動作を担当する部分のこと。アプリケーションの主要な動作やルールを定義するもの。）

**例**: 複数の Model で、特定のカラム（例: **`name`**）の値を大文字に変換して保存したい。


```ruby:app/models/concerns/capitalize_name.rb
module CapitalizeName
  extend ActiveSupport::Concern

  included do
    before_save :upcase_name
  end

  private

  def upcase_name
    self.name = name.upcase
  end
end
```

このモジュールを含む Model は、インクルードすることでそのロジックを再利用できる。

```ruby:
class Product < ApplicationRecord
  include CapitalizeName
  end

class Brand < ApplicationRecord
  include CapitalizeName
  end
```


# helpers

- `app/helpers` に配置されており、view で使うための補助メソッドを提供するもの。主に表示ロジックやマークアップの生成のためのメソッドが扱われる。
- Model に依存しない、アプリケーション全体で使われる可能性のある view 関連の小さなタスクや操作に適してる。

【**例**: 日付を特定のフォーマットで表示するメソッドなど】


```ruby:app/helpers/date_helper.rb

module DateHelper
  def formatted_date(date)
    date.strftime('%Y年%m月%d日')
  end
end

```

# **decorators**

- Model のビジネスロジックと view の表示ロジックを分離するために使われる view ヘルパー。
- Model のデータや関連するロジックを、view 向けに整形・装飾するためのメソッドを書く。
- Rails のデフォルトディレクトリとしては存在しないので、『**`draper`** 』という gem を使用して実装する。

【**例**: ユーザーのフルネームを表示するメソッドなど】

```ruby:app/decorators/user_decorator.rb
class UserDecorator < Draper::Decorator
  delegate_all

  def full_name
    "#{object.first_name} #{object.last_name}"
  end
end

```

# まとめ

**`concerns`**：Controller や Model のビジネスロジックの複雑性や共通性を解消する役割
**`helpers`**：Model に依存せず、一般的な view 関連の小タスクや操作するためのメソッドをまとめる役割
**`decorators`**：Model のデータに依存して、view の表示に関するロジックをまとめる役割
以上のように認識して使い分けたら良さそうだということがわかりました。

# 参考にした記事

[【Rails】decorator, model, helper の使い分け](https://zenn.dev/airx_inc/articles/6400b564396765)

[ActiveSupport::Concern の使い方としくみ - Qiita](https://qiita.com/h-shima/items/d772b4cbe7368ddb8255)

[extend Activesupport::Concern について - Qiita](https://qiita.com/tai_gt/items/52844cad784b67d8bc55)

[helper と decorator の使い分け - Qiita](https://qiita.com/ykemoemo/items/b2c5c68f853a5cc91446)

https://github.com/amatsuda/active_decorator