Using custom validator to compare two form fields

935 Views Asked by At

I'm working on learning custom validations in rails and am attempting to apply it for the purpose of comparing two form fields. So far, I can easily pass one value to the custom my custom validator which I've stored in app/validation/pin_validator.rb and it works fine. But I'd really like to set up something like the following that would accept two pieces of data from the form:

class PinValidator < ActiveModel::EachValidator
  def validate_each (record, attribute, value)
    if value1 > value2
      record.errors[attribute] << (options[:message] || "validator working")
    end
  end
end

And call it from the model with something like this:

validates :pin_number, :id_string, pin: true

Where :id_string and :pin_number are both form fields and would be compared as value1 and value2

Is there a way to do this?

Many thanks in advance!

Note: The value comparison above really could be solved on the front-end with a little javascript. The reason I'm asking for a serverside solution is that I ultimately intend to compare both values with a set of entries in the database. The example above is just to distil my question to something simple.

2

There are 2 best solutions below

7
On BEST ANSWER

You should be able to access attributes listed with @attributes inside the validate_each method, as you can infer from the initializer code here

So you can access record attributes by using values from @attributes which is an array with the attribute names used on the definition of the validation, in your case [:pin_number, :id_string]. You can access them like

def validate_each(record, attribute, value)
   value1 = record[@attributes[0]] # :pin_number
   value2 = record[@attributes[1]] # :id_string
   # the rest of the logic here
end

I realize the validate_each method is going to be run twice as it is supposed to run for each of the fields listed on the validation definition. That might lead to errors being inserted twice or other issues. I am researching if the validator instance is created every time a validation is to be run, if so you could avoid this double validation by setting up a flag, say @already_run and then just returning if that flag was true the second time. As a new instance of the validator would be created next time anything needed to be validated then the flag wouldn't affect anything else.

8
On

Rather than doing the validation on the Rails side you might consider doing the JS approach of validation the values onClick or on some other action that you feel give you the proper control.

All you would need for onClick is a simple method to grab the values from each uniquely named form field and then just compare the val()s of each field and allow/deny submission.

In the case that you need to compare the form fields to a value stored in the DB you can use <input type="hidden> and store that value when the page is loaded so that comparison can still be done in the same manner described above.