Rails validating values in array

26,856

Solution 1

I don't think that default Rails validators will do the trick here, you can do this though:

validate :validate_wdays

def validate_wdays
  if !wdays.is_a?(Array) || wdays.any?{|d| !(0..6).include?(d)}
    errors.add(:wdays, :invalid)
  end
end

Solution 2

I'm not certain whether there are easier ways to handle this within the existing Rails validation structure. You've got an odd case that the validators weren't really built to handle. You might need to write a custom validator for something like this (assuming an existing validation extension gem isn't available). Something like this:

class ArrayInRangeValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, valueArray)
    valueArray.each do |value|
      record.errors.add "#{attribute} - #{value}", (options[:message] || "is not in the acceptable range") unless (1..6).include?(value)
    end
  end
end

and then in your model:

class Schedule < ActiveRecord::Base
    include ActiveModel::Validations

    validates :wdays, :presence => true, :array_in_range => true

    ... other model stuff
end

Solution 3

I created this gem: https://github.com/rafaelbiriba/active_model_validates_intersection_of

Basicaly, you can use like:

class User < ActiveRecord::Base
   DEFAULT_PERMISSION = ["read", "write", "share"]
   validates_intersection_of :permission, in: DEFAULT_PERMISSION
end

and if you try:

user = User.new(permission: ["read", "share"])
user.valid? #true

user = User.new(permission: ["read", "admin"])
user.valid? #false

Enjoy! Comments, pull requests and feedbacks is always welcome.

Solution 4

The accepted answer didn't really do this correctly in my opinion. If someone sets wdays = 'meow' it'll set it to empty array wdays == [] without warning.

Type Casting is happening you ask for wdays in that method. It's already converting the data into an array if it's invalid.

wdays_before_type_cast should be used instead of wdays when checking type.

See _before_type_cast

validate :validate_wdays

def validate_wdays
  errors.add(:wdays, :invalid) if wdays_changed? && !wdays_before_type_cast.is_a?(Array)
  errors.add(:wdays, :invalid) if wdays.any? { |d| (0..6).exclude?(d) }
end
Share:
26,856

Related videos on Youtube

bsiddiqui
Author by

bsiddiqui

Currently hacking @Lob. @Harvard and @hackNY alum

Updated on July 09, 2022

Comments

  • bsiddiqui
    bsiddiqui almost 2 years

    In my Schedule model I want to add some validation to the :wdays field, which is an int[]. I only want values 0..6 to be valid

    Valid

    Schedule.wdays = [0,1,6]
    

    Invalid

    Schedule.wdays = [0,1,10]
    

    I tried using

    validates :wdays, inclusion: { in: [0, 1, 2, 3, 4, 5, 6] }
    

    and

    validates :wdays, inclusion: { in: 0..6 }
    

    but neither works

    What is the proper way to validate the values in an array in your model?

  • Paul Richter
    Paul Richter over 9 years
    @tirdadc I added some of the updates from your edit, however can you verify that the name of the validator must be an underscored version (that it should be array_in_range rather than arrayInRange? Do you know of any documentation on that convention?
  • Vadym Tyemirov
    Vadym Tyemirov about 7 years
    the name of the validator should be underscored
  • Arian Faurtosh
    Arian Faurtosh over 4 years
    This doesn't work fully... if someone sets wdays = 'meow' it'll set it to empty array [] without warning. Something above the stack is happening where it's already converting the data into an array if it's invalid