Ruby 2.6 Range#cover? accepts Range object as argument

Ruby 2.6 enhanced the Range#cover? method to accept Range objects as arguments, making it easier to check if one range covers another range. This is useful for date ranges, number ranges, and other interval comparisons.

Before Ruby 2.6

Previously, cover? only worked with single values:

range = 1..10

range.cover?(5)  # => true
range.cover?(15) # => false

# This didn't work:
range.cover?(3..7) # => NoMethodError or unexpected behavior

Ruby 2.6 Enhancement

Ruby 2.6 allows cover? to accept Range objects:

range = 1..10

range.cover?(5)    # => true (still works)
range.cover?(3..7) # => true (new in Ruby 2.6)
range.cover?(0..5) # => false (0 is outside range)

Basic Usage

Numeric Ranges

Check if one range covers another:

# Check if range covers another range
(1..10).cover?(3..7)   # => true
(1..10).cover?(5..15)  # => false (15 is outside)
(1..10).cover?(0..5)   # => false (0 is outside)

# Check if range covers a single value
(1..10).cover?(5)      # => true
(1..10).cover?(15)     # => false

Date Ranges

Works with date ranges:

date_range = Date.new(2020, 1, 1)..Date.new(2020, 12, 31)

date_range.cover?(Date.new(2020, 6, 15))                    # => true
date_range.cover?(Date.new(2020, 3, 1)..Date.new(2020, 6, 30)) # => true
date_range.cover?(Date.new(2019, 12, 1)..Date.new(2020, 1, 15)) # => false

Real-World Examples

Date Range Validation

Validate that a date range falls within allowed dates:

class Event < ApplicationRecord
  validates :start_date, :end_date, presence: true

  validate :dates_within_allowed_range

  ALLOWED_DATE_RANGE = Date.new(2020, 1, 1)..Date.new(2020, 12, 31)

  private

  def dates_within_allowed_range
    event_range = start_date..end_date

    unless ALLOWED_DATE_RANGE.cover?(event_range)
      errors.add(:base, "Event dates must be within #{ALLOWED_DATE_RANGE}")
    end
  end
end

Price Range Filtering

Check if a price range falls within a filter:

class Product < ApplicationRecord
  def self.in_price_range(min_price, max_price)
    price_range = min_price..max_price

    all.select do |product|
      product.price_range.cover?(price_range)
    end
  end

  def price_range
    sale_price..regular_price
  end
end

Time Slot Validation

Validate time slots:

class Appointment < ApplicationRecord
  BUSINESS_HOURS = Time.parse("09:00")..Time.parse("17:00")

  validate :within_business_hours

  private

  def within_business_hours
    appointment_range = start_time..end_time

    unless BUSINESS_HOURS.cover?(appointment_range)
      errors.add(:base, "Appointment must be within business hours")
    end
  end
end

Comparison with Other Methods

cover? vs include?

range = 1..10

# cover? checks if value/range is within bounds
range.cover?(5)    # => true
range.cover?(3..7) # => true (Ruby 2.6+)

# include? checks membership (uses ===)
range.include?(5)  # => true
range.include?(3..7) # => false (checks if range object is member)

cover? vs ===

range = 1..10

# cover? is more explicit
range.cover?(5) # => true

# === also works but less clear
range === 5 # => true

Advanced Usage

Checking Multiple Ranges

Check if multiple ranges are covered:

def validate_date_ranges(ranges)
  allowed_range = Date.new(2020, 1, 1)..Date.new(2020, 12, 31)

  ranges.all? { |range| allowed_range.cover?(range) }
end

Overlapping Ranges

Check if ranges overlap:

def ranges_overlap?(range1, range2)
  range1.cover?(range2.begin) || range1.cover?(range2.end) ||
  range2.cover?(range1.begin) || range2.cover?(range1.end)
end

Use Cases

Booking System

Validate booking dates:

class Booking < ApplicationRecord
  AVAILABLE_DATES = Date.new(2020, 1, 1)..Date.new(2020, 12, 31)

  validate :dates_available

  private

  def dates_available
    booking_range = check_in..check_out

    unless AVAILABLE_DATES.cover?(booking_range)
      errors.add(:base, "Selected dates are not available")
    end
  end
end

Discount Validation

Validate discount periods:

class Discount < ApplicationRecord
  validate :active_period_within_campaign

  private

  def active_period_within_campaign
    discount_range = start_date..end_date
    campaign_range = campaign.start_date..campaign.end_date

    unless campaign_range.cover?(discount_range)
      errors.add(:base, "Discount period must be within campaign period")
    end
  end
end

Best Practices

  1. Use for range validation: Perfect for checking if ranges are within bounds
  2. Prefer cover? over include?: More explicit for range comparisons
  3. Handle edge cases: Consider inclusive vs exclusive ranges
  4. Document range semantics: Make it clear what ranges represent

Edge Cases

Inclusive vs Exclusive Ranges

# Inclusive range (..)
(1..10).cover?(1..10)  # => true

# Exclusive end range (...)
(1...10).cover?(1...10) # => true
(1...10).cover?(1..9)   # => true
(1...10).cover?(1..10)  # => false (10 is excluded)

Empty Ranges

(1..10).cover?(5..5)   # => true (single value range)
(1..10).cover?(15..20) # => false (completely outside)

Conclusion

Ruby 2.6's enhancement to Range#cover? makes it easier to check if one range covers another, which is particularly useful for date ranges, price ranges, and other interval validations. This feature provides a clean, readable way to validate that ranges fall within expected bounds.

Ruby 2.6 Range#cover? accepts Range object as argument - Abhay Nikam