Home > Back-end >  ActiveRecord Validation of a string but being able to treat part of the string as an integer
ActiveRecord Validation of a string but being able to treat part of the string as an integer

Time:09-21

I'm using Ruby 3.0.2 and trying to validate using ActiveRecord.

I want to validate user input that is a string but want treat the parts of the input as a numbers so that I can apply some logic to it.

The input needs to look something like this

21-10

I will use regex to ensure that there is a '-' between the two numbers, but I want to be able to validate the numbers to ensure that:

  • One of the two sets of numbers is 21
  • Both numbers cannot be larger than 21 unless the difference between them is two apart from each other

My challenge is being able to treat parts of a string as numbers with validation.

An example of input that would be valid/invalid:

11-21 // valid
10-23 // invalid because 23 > 21
11-11 // invalid because neither numbers are 21
22-24 // valid because 22 & 24 within 2 of each other

Any help appreciated!

CodePudding user response:

That can be done with a custom validation method like this:

# in the model
validate :range_string_has_validate_pattern

private

def range_string_has validate_pattern
  parts = range_string.split('-') # assuming the attribute is named `range_string`
  
  if parts.size != 2
    errors.add(:range_string, "does not contain two parts separated by a '-'")
    return
  end

  begin 
    parts[0] = Integer(parts[0])
    parts[1] = Integer(parts[1])
  rescue
    errors.add(:range_string, "does not contain two numbers")
    return
  end

  return if parts[0] == 21 || parts[1] == 21
  return if (parts[0] - parts[1]).abs == 2

  errors.add(:range_string, "does not match requirements")
end

CodePudding user response:

I would go with pattern matching (yay for modern ruby) and a custom validator method

validates :your_attribute, format: { with: /\A\d -\d \z/ }
validate :string_format

def string_format
  # do not attempt if the format is wrong
  return if errors.where(:your_attribute).present?

  case your_attribute.split('-').map(&:to_i)
  in [..20, ..20] 
    error.add(:your_attribute, 'Both numbers are less than 21')
  in [..20, 22..] | [22.., ..20]
    error.add(:your_attribute, 'One is less the other is more')
  in [22.. => x, 22.. => y] if y - x > 2 or x - y > 2
    error.add(:your_attribute, 'Difference is too big')
  else
    return
  end
end
  • Related