This blog is part of our Ruby 2.7 series. Ruby 2.7.0 was released on Dec 25, 2019.

Ruby 2.7 added support for Beginless Range which makes the start of range an optional parameter.

(..100) is a Beginless Range and it is equivalent to (nil..100).

Let’s see how Beginless Range could be used.

> array = (1..10).to_a

# Select first 6 elements
> array[..5]
=> [1, 2, 3, 4, 5, 6]

# Select first 5 elements
> array[...5]
=> [1, 2, 3, 4, 5]

# grep (INFINITY..5) in (1..5)
> (1..10).grep(..5)
=> [1, 2, 3, 4, 5]

# (..100) is equivalent to (nil..100)
> (..100) == (nil..100)
=> true

Here is another example where in the case statement the condition can be read as below the specified level.

case temperature
when ..-15
  puts "Deep Freeze"
when -15..8
  puts "Refrigerator"
when 8..15
  puts "Cold"
when 15..25
  puts "Room Temperature"
when (25..)   # Kindly notice the brackets here
  puts "Hot"
end

It can also be used for defining constants for ranges.

TEMPERATURE = {
  ..-15  => :deep_freeze,
  -15..8 => :refrigerator,
  8..15  => :cold,
  15..25 => :room_temperature,
  25..   => :hot
end

Using Beginless Range in DSL makes it easier to write conditions and it looks more natural.

# In Rails
User.where(created_at: (..DateTime.now))
# User Load (2.2ms)  SELECT "users".* FROM "users" WHERE "users"."created_at" <= $1 LIMIT $2  [["created_at", "2020-08-05 15:00:19.111217"], ["LIMIT", 11]]


# In RubySpec
ruby_version(..'1.9') do
# Tests for old Ruby
end

Here is the relevant commit and discussion regarding this change.