概要
class CommonValidations < NamedValidations
define :required, :presence, true
define :short_text, :length, maximum: 100
define :long_text, :length, maximum: 5_000
define :formatted_text, :format,
with: /\A(?:[^:[:space:]]+:[^\n]*(\n\s+[^\n]*)*\n)*\z/,
allow_blank: true
define :formatted_long_text do |*_args|
long_text.formatted_long_text
end
end
class Article < ApplicationRecord
base = CommonValidations.new
validates :title, base.short_text.required
validates :content, base.long_text(minmum: 100)
validates :meta, base.formatted_long_text
end
説明
Railsのvalidates
の引数に同じような指定がならぶことがあります。もともとの要件が共通なら、コードのほうでも共通にしたい。いろいろなやり方がありそうですが、その一つとして引数のならびに名前を付けられるようにする、というのを作ってみました。
validates :foo, length: { maximum: 100 }, presence: true, ...
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# この部分に名付けする
これはvalidates
の引数を定数にするのに似ています。
VALIDATIONS_HOGE = { length: { maximum: 100 }, presence: true, ... }
validates :foo, VALIDATIONS_HOGE
定数を使った場合、指定内容を少しだけ変えたいときに工夫が必要です。
# ここだけ maximum: 99 にしたい
validates :bar, VALIDATIONS_HOGE.deep_merge(length: { maximum: 99 })
一度きりならともかく、二つ、三つとなると「少しだけ変えたい」だけのわりには記述量が多く、定数にまとめた意味が半減してしいます。
そこで、定数とは別の形で名前を付けることで、部分的な上書き指定をできるようにしてみました。
たとえば上の例のVALIDATIONS_HOGE
にあたる内容にvalidations_hoge
と名付けると、次のように記述できます。
validates :foo, validations_hoge
validates :bar, validations_hoge(maximum: 99)
この例での名付けは以下のように行います。
class CommonValidations < NamedValidations
define :validations_hoge do |*opts|
length({ maximum: 99 }, *opts).presence(true)
end
end
付けた名前を使用するには以下のようにします。
v = CommonValidations.new
validations_hoge = v.validations_hoge
validates :foo, validations_hoge
validations_hoge
にその場で指定を追加することもできます。
v1 = validations_hoge.format(with: /@/)
v2 = v1.length(minimum: 5)
validates :bar, v1
validates :baz, v2.presence(if: ...)
使用例
concerns/common_validations.rb
module CommonValidations
class Validations < NamedValidations
define :short_text, length(maximum: 80)...
define :long_text, length(maximum: 1000)...
define :email do |*opts|
short_text.format({ with: /@/, allow_blank: true }, *opts)
end
end
class_methods do
def vals
Validations.new.presence(true)
end
# この例では起点だけメソッドにしているが、
# short_text、long_textなどすべてメソッドにしてもよい。
# Validations.aliases - NamedValidations.aliases で定義リストを得られる。
end
end
post.rb
class Post < ApplicationRecord
include CommonValidations
validates :title, vals.short_text
validates :author_name, vals.short_text
validates :author_mail, vals.email
end