Railsのストロングパラメータ（Strong Parameters）を一括で処理する書き方

Railsのリファクタリング記事です。
RailsでStrong Parametersを書くときに入力項目が多いページだと記述が多くなって「なんだかなぁ…」と思ったので、スッキリさせてみました。

テーブルからカラム名を引っ張ってゴニョゴニョして出力しています。
結論をすぐに知りたい方は後半の[Strong Parametersをスッキリさせてみた](#strong-parametersをスッキリさせてみた)へどうぞ。

## Strong Parametersってなんだっけ？
Strong Parametersを使ってない人はいないと信じたいのですが、確認も含めて簡単に説明しておきます。
Strong ParametersはRails4系から追加されたセキュリティを向上させるための仕組みです。
こちらで指定したパラメータ以外は受け取らないようにして、攻撃者による意図しないコードの実行を防止するセキュリティ対策。

実際にどうやって使うのか。
例えば、送信された値からユーザーを作成するようなケースでは以下のように利用されます。

```app/controllers/users_controller.rb
class UsersController < ApplicationController
  def create
    user = User.new(user_params)
  end

  private

  def user_params
    params.require(:user).permit(:name, :email)
  end
end
```

これが基本系で、送信されてくるパラメータに`:user`というキーが必須です。
`:user`はハッシュ型の値を持っていて、このハッシュには`:name`と`:email`のキーのみを許可するという設定になりました。

## モデルによって記述量が増える問題

通常、上記のような使い方をしますが、テーブルのカラム数が多くなると記述する値が増えてしまいます。
そんなにたくさんの項目を「1つのStrong Parametersに持たせる設計」にするべきかは別の問題としてありますが…
それは一旦置いておくとして、カラム分を追加していくと以下のような感じになると思います。

```app/controllers/users_controller.rb
def user_params
  params.require(:user).permit(:screen_name, :name, :kana, :nickname, :tel, :email, :address, :building, :gender, :birth_day)
end
```

このように、たくさん書かないといけません。面倒。
一行で記述すると読みづらいので、僕は今まで下記のように書いていました。

```app/controllers/users_controller.rb
def user_params
  params.require(:user).permit(
    :screen_name, :name, :kana, :nickname,
    :tel, :email, :address, :building,
    :gender, :birth_day,
  )
end
```


1つのアプリケーションで入力があるモデルは複数になることが多いと思います。
モデルが増えていくと、同様にモデルごとに大量のStrong Parametersを書くことになってしまい非常に手間です。

今回はこれを便利な感じにリファクタリングしてスッキリさせてみました。

## Strong Parametersをスッキリさせてみた

上記のようにStrong Parametersに記述する値は**モデルのテーブル名と一致することが多い**と思います。（一致していないとRailに乗っていないような…）

これを利用して、**モデルのテーブル名から`column_names`を使って一括で出力**します。
`column_names`はActiveRecord::Baseに用意されているメソッドです。

`column_names`で出力されるのは文字列型の配列なので、**どのモデルからでもシンボル型の配列で取得できる共通のクラスメソッド**を用意しておきます。
ついでにリストから除外したいカラム名を配列で渡せるような引数（reject）も用意します。

```app/models/concerns/common_module.rb
module CommonModule
  extend ActiveSupport::Concern

  module ClassMethods
    # カラム名のシンボル型の配列
    def column_symbolized_names(reject=[])
      column_names.delete_if {|n| n.in?(reject)}.map(&:to_sym)
    end
  end
end
```

このModuleを共通メソッドとしてモデルで読み込みます。

```app/models/user.rb
class User < ApplicationRecord
  # 共通設定の読み込み
  include CommonModule
end
```


あとは用意しておいた**クラスメソッドでカラム名をそのままStrong Prametersに格納します。**

```app/controllers/users_controller.rb
def user_params
  columns = User.column_symbolized_names
  params.require(:user).permit(*columns)
end
```

>（注意）
通常の方法であれば`updated_at`と`created_at`はStrong Parametersには含めなくてもデフォルト値を入れてくれるので、記述は不要なのですが、同様にrejectに含めてしまうと、保存時にMySQLから`Field 'created_at' doesn't have a default value`と怒られてしまいました。
Railsの仕様なのでしょうか…理由がよくわからなかったのですが、ご存知の方がいたら教えてください:bow:

ただし、この方法でもStrong Parametersに不要なカラムや、配列・連想配列を持ちたいパラメータには対応しないので、そこだけはカスタマイズします。
例えば、配列を保持するtagsパラメータが必要だとしたら…

```app/controllers/users_controller.rb
def user_params
  reject = %w(tags)  # 出力する配列からimagesを除外しておく
  columns = User.column_symbolized_names(reject).push(tags: [])
  params.require(:user).permit(*columns)  # 出力された配列にPush
end
```

tagsカラムを一度rejectして、最後にcolumnsに追加するだけ。

メタプログラミングを駆使すればもう少し共通化できそうな気もしますが、柔軟性も担保する場合はこのぐらいの共通化、簡略化が妥当かなと思います。

上記のように共通化することで、様々なケースに**柔軟に対応可能**で**ほぼほぼコピペで使いまわせちゃう**Strong Parametersを作ることができました。