1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Fix bug in Range comparisons when comparing to excluded-end Range

Before:
```ruby
(1..10).cover?(1...11) => false
```

After:
```ruby
(1..10).cover?(1...11) => true
```

See https://git.io/fjTtz for the commit against Ruby core that added
support for Range arguments, with similar handling of this case.
This commit is contained in:
Owen Stephens 2019-03-28 00:57:05 +00:00
parent 5f043c0094
commit ca2a3bcaad
3 changed files with 41 additions and 14 deletions

View file

@ -1,3 +1,17 @@
* Fix bug in Range comparisons when comparing to an excluded-end Range
Before:
(1..10).cover?(1...11) => false
After:
(1..10).cover?(1...11) => true
With the same change for `Range#include?` and `Range#===`.
*Owen Stephens*
* Use weak references in descendants tracker to allow anonymous subclasses to
be garbage collected.

View file

@ -3,9 +3,10 @@
module ActiveSupport
module CompareWithRange
# Extends the default Range#=== to support range comparisons.
# (1..5) === (1..5) # => true
# (1..5) === (2..3) # => true
# (1..5) === (2..6) # => false
# (1..5) === (1..5) # => true
# (1..5) === (2..3) # => true
# (1..5) === (1...6) # => true
# (1..5) === (2..6) # => false
#
# The native Range#=== behavior is untouched.
# ('a'..'f') === ('c') # => true
@ -13,17 +14,20 @@ module ActiveSupport
def ===(value)
if value.is_a?(::Range)
# 1...10 includes 1..9 but it does not include 1..10.
# 1..10 includes 1...11 but it does not include 1...12.
operator = exclude_end? && !value.exclude_end? ? :< : :<=
super(value.first) && value.last.send(operator, last)
value_max = !exclude_end? && value.exclude_end? ? value.max : value.last
super(value.first) && value_max.send(operator, last)
else
super
end
end
# Extends the default Range#include? to support range comparisons.
# (1..5).include?(1..5) # => true
# (1..5).include?(2..3) # => true
# (1..5).include?(2..6) # => false
# (1..5).include?(1..5) # => true
# (1..5).include?(2..3) # => true
# (1..5).include?(1...6) # => true
# (1..5).include?(2..6) # => false
#
# The native Range#include? behavior is untouched.
# ('a'..'f').include?('c') # => true
@ -31,17 +35,20 @@ module ActiveSupport
def include?(value)
if value.is_a?(::Range)
# 1...10 includes 1..9 but it does not include 1..10.
# 1..10 includes 1...11 but it does not include 1...12.
operator = exclude_end? && !value.exclude_end? ? :< : :<=
super(value.first) && value.last.send(operator, last)
value_max = !exclude_end? && value.exclude_end? ? value.max : value.last
super(value.first) && value_max.send(operator, last)
else
super
end
end
# Extends the default Range#cover? to support range comparisons.
# (1..5).cover?(1..5) # => true
# (1..5).cover?(2..3) # => true
# (1..5).cover?(2..6) # => false
# (1..5).cover?(1..5) # => true
# (1..5).cover?(2..3) # => true
# (1..5).cover?(1...6) # => true
# (1..5).cover?(2..6) # => false
#
# The native Range#cover? behavior is untouched.
# ('a'..'f').cover?('c') # => true
@ -49,8 +56,10 @@ module ActiveSupport
def cover?(value)
if value.is_a?(::Range)
# 1...10 covers 1..9 but it does not cover 1..10.
# 1..10 covers 1...11 but it does not cover 1...12.
operator = exclude_end? && !value.exclude_end? ? :< : :<=
super(value.first) && value.last.send(operator, last)
value_max = !exclude_end? && value.exclude_end? ? value.max : value.last
super(value.first) && value_max.send(operator, last)
else
super
end

View file

@ -57,7 +57,7 @@ class RangeTest < ActiveSupport::TestCase
end
def test_should_include_other_with_exclusive_end
assert((1..10).include?(1...10))
assert((1..10).include?(1...11))
end
def test_should_compare_identical_inclusive
@ -69,7 +69,7 @@ class RangeTest < ActiveSupport::TestCase
end
def test_should_compare_other_with_exclusive_end
assert((1..10) === (1...10))
assert((1..10) === (1...11))
end
def test_exclusive_end_should_not_include_identical_with_inclusive_end
@ -93,6 +93,10 @@ class RangeTest < ActiveSupport::TestCase
assert range.method(:include?) != range.method(:cover?)
end
def test_should_cover_other_with_exclusive_end
assert((1..10).cover?(1...11))
end
def test_overlaps_on_time
time_range_1 = Time.utc(2005, 12, 10, 15, 30)..Time.utc(2005, 12, 10, 17, 30)
time_range_2 = Time.utc(2005, 12, 10, 17, 00)..Time.utc(2005, 12, 10, 18, 00)