カスタムFormは、Concernに項目ごと分割して作ってincludeさせれば幸せになれるかと思った話


ふわっとしたタイトルですいません。

Modelとイコールでない入力Formを作る際に自分はカスタムFormを作ったりします。
そこでModelで定義したルールを内包出来る場合は問題ないけど、内包できないケースだとルールを複数書かなくてはならず地味にめんどい。

そんな訳で項目ごとにConcernsにModule作って、ModelとカスタムFormそれぞれにincludeさせれば無駄が無いんじゃと思った話。

具体的な実装は以下の通り。

```app/models/concerns/form_items/mail_addresss.rb
module FormItems::MailAddress

  extend ActiveSupport::Concern

  included do
    attr_accessor :mail_address, :mail_address

    validates :mail_address,
      presence: { message: 'メールアドレスを入力してください。' }
  end
  
  def mail_address_domain
    return self.mail_address.split(/@/).last
  end

end
```

`attr_accessor`や`validates`などの定義は`included do`のブロック内で行い、インスタンスメソッドは普通に定義する。

ModelやカスタムFormからはincludeするだけ。

```app/models/user.rb
class User < ApplicationRecord

  include FormItems::MailAddress

end
```

カスタムForm Modelは`ActiveModel::Model`をincludeして作成。

```app/models/forms/user_input.rb
class Forms::UserInput

  include ActiveModel::Model

  include FormItems::MailAddress

end
```

実行結果は以下の通り。

```
# Model
[1] pry(main)> user = User.new
[2] pry(main)> user.valid?
[3] pry(main)> user.errors
=> #<ActiveModel::Errors:0x007ff7ee66d260
 @base=
  #<User:0x007ff7f1f098a8
   id: nil,
   mail_address: nil,
   created_at: nil,
   updated_at: nil>,
 @details={:mail_address=>[{:error=>:blank}]},
 @messages={:mail_address=>["メールアドレスを入力してください。"]}>
 
# カスタムForm Model
[4] pry(main)> form = Forms::UserInput.new
[5] pry(main)> form.valid?
[6] pry(main)> form.errors
=> #<ActiveModel::Errors:0x007ff7ee596b98
@base=#<Forms::UserInput:0x007ff7ee5cfd30 @errors=#<ActiveModel::Errors:0x007ff7ee596b98 ...>, @validation_context=nil>,
 @details={:mail_address=>[{:error=>:blank}]},
 @messages={:mail_address=>["メールアドレスを入力してください。"]}>

# インスタンスメソッドも使える
[7] pry(main)> form.mail_address = 'hoge@piyo.com'
[8] pry(main)> form.mail_address_domain
=> "piyo.com"
```

１項目１モジュールを全部作ると逆に面倒な気もするが、セットで使うのが当たり前の項目はまとめても大丈夫な気がする。

```app/models/concerns/form_items/name.rb
module FormItems::Name

  extend ActiveSupport::Concern

  included do
    attr_accessor :name_sei, :name_sei
    attr_accessor :name_mei, :name_mei

    validates :name_sei,
      presence: { message: '名前(姓)を入力してください。' }

    validates :name_mei,
      presence: { message: '名前(名)を入力してください。' }
  end

  def full_name
    return "#{self.name_sei} #{self.name_mei}"
  end

end
```

最終的には以下の様に出来れば見た目もすっきりしていいんじゃないかな、と。
まぁ、実際は色々な処理が入り込んで来るのでここまで綺麗に出来ませんでしたが。。。

```
# User モデル
class User < ApplicationRecord

  include FormItems::Name
  include FormItems::MailAddress
  include FormItems::Sex
  include FormItems::Prefecture
  include FormItems::City
  include FormItems::AddressText

end

# カスタムForm 1
class Forms::UserInputFirst

  include ActiveModel::Model

  include FormItems::Prefecture
  include FormItems::MailAddress

end

# カスタムForm 2
class Forms::UserInputSecond

  include ActiveModel::Model

  include FormItems::Name
  include FormItems::Sex
  include FormItems::City
  include FormItems::AddressText

end
```

というのを同僚に話してみたが、反応がイマイチだったので判断に迷う今日この頃。
例外的なFormがいくつも誕生するのは設計の問題じゃないのか？と言われたらぐぅの音も出ない。

でも、入力ハードル下げるためとか大人の事情で急遽類似するFormを量産する事ってよくありませんか？
弊社だけですか。そうですか。


ちなみにConcern使ってModelを分割するのは、concerned_withパターンと言うらしい。

その割にあまり使われているトコを見ないし(自分の周りだけか？)、ググってもあまり情報が出てこないので、ひょっとしてアンチパターンなんだろうか。。。