devOrange
devOrange

Reputation: 11

Pass input params to custom validation function ruby

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

Answers (1)

BenFenner
BenFenner

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

Related Questions