Search code examples
ruby-on-railsrubyrefactoringcode-duplication

Reduce unnecessary duplication between two classes


I have two classes responsible for attributes validation:

class NameValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    message = options.fetch(:message, I18n.t('errors.attributes.name.invalid'))
    record.errors[attribute] << message unless NameValidator.valid_name?(value)
  end

  def self.valid_name?(name)
    name =~ /\A[a-z][\w\p{Blank}]+\z/i
  end
end

and the second one

class EmailValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    message = options.fetch(:message, I18n.t('errors.attributes.email.invalid'))
    record.errors[attribute] << message unless EmailValidator.valid_email?(value)
  end

  def self.valid_email?(email)
    email =~ /\A.+@.+\..+\z/i
  end
end

They're basically the same. Should I inherit them from one class with protected utility methods or what?


Solution

  • You can simplify this further

    class PatternValidator < ActiveModel::EachValidator
      def validate_each(record, attribute, value)
        message = options.fetch(:message) || kind
        record.errors[attribute] << message unless value =~ validation_pattern
      end
    end
    
    class NameValidator < PatternValidator
      def validation_pattern; /\A[a-z][\w\p{Blank}]+\z/i end
    end
    
    class EmailValidator < PatternValidator
      def validation_pattern; /\A.+@.+\..+\z/i end
    end
    

    EachValidator has a #kind method so it will add :name or :email as the failure message unless overridden. Then you can leave the i18n to do the lookup as per the standard cascade as documented in rails guide.