LoginSignup
4
3

More than 5 years have passed since last update.

[Rails]ActiveModelで違うColumn名でも同じvalidationルールを共有したい

Last updated at Posted at 2017-11-25

同じ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名で共通化したい人はこちらのほうが良いかもしれませんね。

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3