Search code examples
ruby-on-railsvalidationactivemodel

Rails where to place your Activemodel::validators


This is a two part question.

Part one if my Model that i am writing the validations for is inhering from ActiveRecord::Base do i need to include ActiveModel::Validations within that class?? the API for rails doesnt say but here in yehudakatz blog seems to hint that?

part two is where would be the best place to put these validator files? under helpers or as a new model or in lib?

my current validator looks like this

class GenderValidator < ActiveModel::validator
  def validate(record)
    cred = /(\d{6})(\d{4})(\d{1})(\d{2})/.match(record.id_number.to_s) #breaks up the id into the relevent sections namely birthdate, gender, nationality, validator.
    unless cred[0][/\d{13}/] #checks to see if the id is a valid length of numbers if it isnt then skip the validation of gender
      return true
    else
      birthdate = cred[1] #returns a 6 digit string 'yymmdd'
      parsed_gender = cred[2] #returns a 4 digit string '0001-4999:female 5000-9999:male'
      nationality = cred[3] # should return either a 1 or a 0 1 if the person is foreign or 0 if the person is southafrican

      validate_gender(parsed_gender, record)
    end
  end

  private
  def validate_gender(parsed_gender, record)
    calculate_gender = (parsed_gender <= 4999 ? :female : :male)
      unless employee.gender == calculate_gender
        employee.errors[:gender] << "Your id indicates you have entered the wrong gender"
      end
  end
end

an valid id number of each person is optional, but if they do specify it it should check to see if the gender is correct.

if i keep it in the same model the employees model then i get this error

ActionController::RoutingError (uninitialized constant Employee::GenderValidator):
app/models/employee.rb:25:in `<class:Employee>'
app/models/employee.rb:1:in `<top (required)>'
lib/role_requirement_system.rb:19:in `inherited'
app/controllers/employees_controller.rb:1:in `<top (required)>'
librato-rails (0.8.1) lib/librato/rack/middleware.rb:12:in `call'

so i take it they cant be in the same file. what is the best practice for validations? i watched all the rails casts and i have read a few blogs and i am quite new still.

EDIT

in my model i include this class like

include ActiveModel::Validations

and my validations look like this

validates_presence_of :name, :position, :gender
validate :instance_validations, :on => :create

def instance_validations
  validates_with GenderValidator    
end

just incase you wanted to see that too thanks ahead!


Solution

  • You do not need include ActiveModel::Validations

    My preference to to keep the validation objects in the model folder.

    So for model Gender you have a file gender.rb

    For the validator GenderValidator you have the file gender_validator.rb

    So both files site together in the model folder.

    Here is a validator for my Newsletter model

    class NewsletterValidator < ActiveModel::Validator
      def validate(record)
          if record.send_test_email
            if test_email_address.blank?
              record.errors[:test_email_address] << "Test email address is blank"
            end
            if record.send_email_to_subscribers
              record.errors[:send_test_email] << "You cannot send a test and send to subscribers at the same time"
            end
          end
    
      end
    end
    

    In my Newsletter model I simply have

      validates_with NewsletterValidator
    

    You have a spelling mistake in your example

    You have

    class GenderValidator < ActiveModel::validator
    

    It should be

    class GenderValidator < ActiveModel::Validator
    

    Note the capital V