1

I have two custom validation method for two class attributes. Both share the same code, but for different attrs. Could you help me reduce code duplication here?

validates validation_fcn_for_attr_1
validates validation_fcn_for_attr_2


def validation_fcn_for_attr1:
.
<same body with attr1>
.
end

def validation_fcn_for_attr2:
.
<same body with attr2>
.
end

Here is my use case:

validate :verify_valid_locations
validate :verify_loactions_for_vacation
validate :another_validation_fcn

validates :travel_dist, number: {greater_than: 0}

    def verify_valid_locations
        acceptable_locations = ["C", "T", "G"]
        unless locations.in? acceptable_locations
          errors.add(:base, 'Acceptable towns for move are "C", "T", "G"')
        end
    end

    def verify_loactions_for_vacation
        acceptable_locations = ["C", "T", "G"]
        unless vacation_locations.in? acceptable_locations
          errors.add(:base, 'Acceptable towns for paid vacation are "C", "T", "G"')
        end
    end
 
0

1 Answer 1

2

Edit: Now that you've provided your real code, I see you're essentially doing an inclusion validation, you can do that with the built-in Rails validation techniques. Here's how:

validates :locations,
          :vacation_locations,
          inclusion: { in: ['C', 'T', 'G'],
                       message: 'Acceptable towns are "C", "T", "G"' }

Make note the errors won't be added to the base attribute, they will be added to the actual attributes. This is almost always more desirable.
(You might consider tossing the message into the locale file, or modifying the existing message there, but that's a discussion for another day.)


There are a few ways to accomplish this. If you provide a bit more detail about your actual example I can help more. But for now you could setup a helper method for the shared code and call it from both of the validation methods:

# app/models/model_name.rb
validate :validation_fcn_for_attr_1
validate :validation_fcn_for_attr_2

def validation_fcn_for_attr1
  helper(attr1)
end

def validation_fcn_for_attr2
  helper(attr2)
end

def helper(attr)
  .
  <shared body>
  .
end
private :helper

Or if the validation is simple enough, you might prefer:

# app/models/model_name.rb
validate :validation_fcn

def validation_fcn
  [attr_1, attr_2].each do |attr|
    .
    <shared body>
    .
  end
end
private :validation_fcn

Or you could use a validates_each block:

validates_each :attr_1, :attr_2 do |record, attr, value|
  .
  <shared body>
  .
end

Or you could create a full on proper custom validator and call it on both attributes. Let me know if you'd like to go that route and I can help there. (It might be easier if you post something closer to your actual validation technique and/or attribute names.) Ultimately that would look something like this:

# app/models/model_name.rb
validates :attr_1,
          :attr_2,
          custom_validator_name: true
# app/validators/custom_validator_name.rb
class CustomValidatorName < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    .
    <validation code>
    .
  end
end
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you. This does help. I prefer the first approach with the helper function since I would like to avoid creating the class. As a follow-up question, is it possible to parametrize a validation method? For example, have something like validation_fcn(attr)
Passing attributes into the validation method can't be done, I don't think. But I will double check tomorrow. The normal way to do that is with a proper custom validator (final example in my answer). I have edited my answer to give you another possibility (2nd option) that you might prefer, so check that out. Also, what are you validating? It's possible it can be done with the built-in validations. They are maybe more powerful than you realize.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.