Ruby ranges are very handy and efficient when you want to work with a sequence of numbers or characters bound by start and end values. But we could come across situations where we want ranges that either have no end value or start value. Ruby 2.6 introduced the concept of an endless range and later Ruby 2.7 brought in the beginless range.
Infinite Ranges in Ruby
As mentioned in the official Ruby docs, a beginless range
and endless range
represents a semi-infinite range.
A beginless range would look like (..3)
Or (...3)
while endless range looks like (3..)
Or (3...)
and effectively defines the maximum or minimum boundary of the range.
Infinite Ranges in Rails
Infinite ranges are supported in Rails while fetching records from the database as seen below:
Loading development environment (Rails 7.1.0.alpha)
irb(main):013:0> Event.where(date: (..Time.now))
Event Load (0.5ms) SELECT "events".* FROM "events" WHERE "events"."date" <= ? /* loading for pp */ LIMIT ? [["date", "2022-12-11 12:56:31.866828"], ["LIMIT", 11]]
=>
[#<Event:0x000055a3f20b22f0
id: 1,
date: Sun, 11 Dec 2022 12:32:57.255815000 UTC +00:00,
name: "The December Concert",
description: "The ultimate concert to end the year",
created_at: Sun, 11 Dec 2022 12:32:57.256643000 UTC +00:00,
updated_at: Sun, 11 Dec 2022 12:32:57.256643000 UTC +00:00>,
#<Event:0x000055a3f20b2188
id: 5,
date: Mon, 31 Oct 2022 18:30:00.000000000 UTC +00:00,
name: "The Halloween Festival",
description: "The eve of All Saints Day",
created_at: Sun, 11 Dec 2022 12:55:33.831731000 UTC +00:00,
updated_at: Sun, 11 Dec 2022 12:56:21.981937000 UTC +00:00>]
The above query fetched all the past event records, i.e the ones with the date
value less than the current time.
Loading development environment (Rails 7.1.0.alpha)
irb(main):014:0> Event.where(date: (Time.now..))
Event Load (0.2ms) SELECT "events".* FROM "events" WHERE "events"."date" >= ? /* loading for pp */ LIMIT ? [["date", "2022-12-11 12:56:43.438138"], ["LIMIT", 11]]
=>
[#<Event:0x000055a3f26616b0
id: 2,
date: Sat, 31 Dec 2022 18:30:00.000000000 UTC +00:00,
name: "The New Year Event",
description: "The first event of the year",
created_at: Sun, 11 Dec 2022 12:34:23.292070000 UTC +00:00,
updated_at: Sun, 11 Dec 2022 12:34:23.292070000 UTC +00:00>,
#<Event:0x000055a3f26615e8
id: 3,
date: Thu, 12 Jan 2023 18:30:00.000000000 UTC +00:00,
name: "The Lohri Festival",
description: "The celebration of harvest festival",
created_at: Sun, 11 Dec 2022 12:36:13.234757000 UTC +00:00,
updated_at: Sun, 11 Dec 2022 12:37:13.423191000 UTC +00:00>]
The above query fetched all the future event records, i.e the ones with the date
value greater than the current time.
In addition to using ranges for fetching records, we generally use them in Rails with the Active Record length and inclusion validators.
With Rails < 7.1.0-alpha
If we use infinite range in a validator like:
class Event < ApplicationRecord
...
validates_length_of :name, in: (..30)
...
end
it throws an error as follows:
Loading development environment (Rails 7.0.1)
irb(main):001:0> Event
Traceback (most recent call last):
3: from (irb):1
2: from app/models/event.rb:4:in `<main>'
1: from app/models/event.rb:7:in `<class:Event>'
RangeError (cannot get the minimum of beginless range)
The only way to validate is by passing maximum
or minimum
value to the validates
method as follows:
validates_length_of :name, maximum: 30, allow_blank: true
With Rails >= 7.1.0-alpha
With the two PRs that were merged recently - PR#45123 and PR#45138, it is possible to pass beginless/endless ranges to the length and inclusivity validators with the in/within
options.
# Allows date to be either today's or any future date but not a past date
validates_inclusion_of :date, in: -> { (Date.today..) }
# Allows name to be nil or of a max length of 30 characters
validates_length_of :name, in: (..30)
# Mandates description to be of minimum 10 characters with no max limit
validates_length_of :description, in: (10..)
Please note: These changes are available in the alpha phase of Rails 7.1.0 at the time of writing this article. To get the 7.1.0-alpha version, you have to specify the rails main branch in Gemfile -
# Gemfile
gem 'rails', github: 'rails/rails', branch: 'main'