Rails now supports infinite range options in LengthValidators
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
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK