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
- Use for range validation: Perfect for checking if ranges are within bounds
- Prefer cover? over include?: More explicit for range comparisons
- Handle edge cases: Consider inclusive vs exclusive ranges
- 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.