ふわっとしたタイトルですいません。
Modelとイコールでない入力Formを作る際に自分はカスタムFormを作ったりします。
そこでModelで定義したルールを内包出来る場合は問題ないけど、内包できないケースだとルールを複数書かなくてはならず地味にめんどい。
そんな訳で項目ごとにConcernsにModule作って、ModelとカスタムFormそれぞれにincludeさせれば無駄が無いんじゃと思った話。
具体的な実装は以下の通り。
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するだけ。
class User < ApplicationRecord
include FormItems::MailAddress
end
カスタムForm ModelはActiveModel::Model
をincludeして作成。
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"
1項目1モジュールを全部作ると逆に面倒な気もするが、セットで使うのが当たり前の項目はまとめても大丈夫な気がする。
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パターンと言うらしい。
その割にあまり使われているトコを見ないし(自分の周りだけか?)、ググってもあまり情報が出てこないので、ひょっとしてアンチパターンなんだろうか。。。