同じvalidationルールだけど、違うColumn名でvalidationしたい状態
RailsでActiveModelを書いてると同じvalidationのルールだけど、カラム名が違うってことあると思います。例えば次の2つのModel Foo
, Bar
があるとします。
app/model/foo.rb
class Foo < ActiveModel::Base
validates :phone_hoge, present: true, format: { with: /\A0\d{9,10}\Z/ }
validates :column_piyo, present: true
# ...
end
app/model/bar.rb
class Bar < ActiveModel::Base
validates :phone_fuga, present: true, format: { with: /\A0\d{9,10}\Z/ }
validates :column_toto, present: true
# ...
end
この2つのModelでは phone_hoge
, phone_fuga
という2つのColumnがあり、そのどちらもが同じValidationルールを持っています。この2つのValidationルールを一緒に管理したい場合はどうすれば良いのでしょうか?
CustomValidatorを作りましょう
ActiveModel::Validator
を継承した新たなValidationClassをつくり、各Modelで validates_with
でvalidateします。
app/model/validator/phone_validator.rb
class PhoneValidator < ActiveModel::Validator
def validate(record)
i18n_message_path = "activerecord.errors.models.#{record.class.name.underscore}.attributes.#{options[:column_name].to_s}.not_allow_format"
unless present_checker(record[options[:column_name]])
record.errors[options[:column_name]] << I18n.t(i18n_message_path + '.blank')
end
unless format_checker(record[options[:column_name]])
record.errors[options[:column_name]] << I18n.t(i18n_message_path + '.not_allow_format')
end
end
private
def present_checker(str)
return false if str.nil? || !str.is_a?(String)
true
end
def format_checker(str)
return false if (str =~ /\A0\d{9,10}\Z/).nil?
true
end
end
app/model/foo.rb
class Foo < ActiveModel::Base
validates_with PhoneValidator, column_name: :phone_hoge
validates :column_piyo, present: true
# ...
end
app/model/bar.rb
class Bar < ActiveModel::Base
validates_with PhoneValidator, column_name: :phone_fuga
validates :column_toto, present: true
# ...
end
Ruleの表現がめんどくさいので、 validates
を使ってCustomValidatorを作りたい人へ
このようなのはどうでしょう? CustomValidator
内で宣言している phone_validates
自体は内部で validates
を利用しているので、 validates
が提供しているオプションをそのまま利用できます。
app/model/concern/custom_validator.rb
require 'active_support'
module CustomValidator
extend ActiveSupport::Concern
class_methods do
def phone_validates(col_name, *options)
arg = options.extract_options!
send(
:validates,
col_name,
presence: arg[:precense],
format: { with: /\A0\d{9,10}\Z/ }
)
end
end
end
app/model/foo.rb
class Foo < ActiveModel::Base
include CustomValidator
phone_validates :phone_hoge, precense: true
validates :column_piyo, present: true
# ...
end
app/model/bar.rb
class Bar < ActiveModel::Base
include CustomValidator
phone_validates :phone_fuga, precense: true
validates :column_toto, present: true
# ...
end
validates
を違うColumn名で共通化したい人はこちらのほうが良いかもしれませんね。