3

Rails now supports infinite range options in LengthValidators

 1 year ago
source link: https://blog.saeloun.com/2022/10/19/infinite-range
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Rails now supports infinite range options in LengthValidators

Oct 19, 2022 , by Swaathi Kakarla

2 minute read

Rails model validations have a host of options that allow developers to write complex rules for clean data entry. A common validation is to ensure that the length of a string is within a certain range. For example, a user’s name should be between 2 and 20 characters. This can be achieved using the length validator.

app/models/user.rb

  class User
    validates_length_of :first_name, in: 2..30
  end

For most part this gets the job done. When a user model is created with a name less than 2 characters or more than 30 characters, an appropriate error is thrown,

  irb(main):001:0> User.create! first_name: "A"
  /Users/swaathi/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/bundler/gems/rails-c41e2f1bca65/activerecord/lib/active_record/validations.rb:82:in `raise_validation_error': Validation failed: First name is too short (minimum is 2 characters) (ActiveRecord::RecordInvalid)

  irb(main):002:0> User.create! first_name: "A"*31
  /Users/swaathi/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/bundler/gems/rails-c41e2f1bca65/activerecord/lib/active_record/validations.rb:82:in `raise_validation_error': Validation failed: First name is too long (maximum is 30 characters) (ActiveRecord::RecordInvalid)

Before

However not all attributes require a minimum or maximum length. For example, a middle name can have a maximum length of 30 characters, but it can be empty. This is not possible with the current implementation of the length validator.

The length validation or a custom one would have to be written.

  class User
    validates :middle_name, length: { maximum: 30 }
    validate :middle_name_length

    private

    def middle_name_length
      if middle_name.present? && middle_name.length > 30
        errors.add(:middle_name, "is too long (maximum is 30 characters)")
      end
    end
  end

After

Thanks to this PR which introduces extensions to the Range class, the first or the last value of a range can be infinite. This allows us to write the following validations,

  class User
    validates_length_of :first_name, in: 2..
    validates_length_of :middle_name, in: ..30
  end

This allows validations to be written in a more concise way. The length validator now supports infinite ranges in the :in and :within options.

This change is possible due to the Float::INFINITY option which is a number that is always the largest in a comparison. This is how the code looks now,

activemodel/lib/active_model/validations/length.rb

  def initialize(options)
    if range = (options.delete(:in) || options.delete(:within))
      raise ArgumentError, ":in and :within must be a Range" unless range.is_a?(Range)
      options[:minimum] = range.min if range.begin
      options[:maximum] = (range.exclude_end? ? range.end - 1 : range.end) if range.end
    end

    if options[:allow_blank] == false && options[:minimum].nil? && options[:is].nil?

    ...

  end
    
  def check_validity!
    keys.each do |key|
      value = options[key]

      unless (value.is_a?(Integer) && value >= 0) ||
              value == Float::INFINITY || value == -Float::INFINITY ||
              value.is_a?(Symbol) || value.is_a?(Proc)
        raise ArgumentError, ":#{key} must be a non-negative Integer, Infinity, Symbol, or Proc"
      end
    end

    ...

  end

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK