Reputation: 11
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
Upvotes: 1
Views: 1298
Reputation: 1068
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
Upvotes: 2