Fix issues with ActiveSupport Range extensions on boundless Ranges

Address complaints by CodeClimate

Address review feedback
This commit is contained in:
Andrew Hodgkinson 2019-09-12 16:32:57 +12:00
parent 6bf2e59bec
commit a7d0916410
3 changed files with 70 additions and 3 deletions

View File

@ -1,3 +1,8 @@
* Fix `Range#===`, `Range#include?`, and `Range#cover?` to work with beginless (startless)
and endless range targets. Builds on work by Allen Hsu.
*Andrew Hodgkinson*
* Fix `Range#include?` to work with beginless and endless ranges.
*Allen Hsu*

View File

@ -11,13 +11,15 @@ module ActiveSupport
# The native Range#=== behavior is untouched.
# ('a'..'f') === ('c') # => true
# (5..9) === (11) # => false
#
# The given range must be fully bounded, with both start and end.
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? ? :< : :<=
value_max = !exclude_end? && value.exclude_end? ? value.max : value.last
super(value.first) && value_max.send(operator, last)
super(value.first) && (self.end.nil? || value_max.send(operator, last))
else
super
end
@ -32,13 +34,15 @@ module ActiveSupport
# The native Range#include? behavior is untouched.
# ('a'..'f').include?('c') # => true
# (5..9).include?(11) # => false
#
# The given range must be fully bounded, with both start and end.
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? ? :< : :<=
value_max = !exclude_end? && value.exclude_end? ? value.max : value.last
super(value.first) && value_max.send(operator, last)
super(value.first) && (self.end.nil? || value_max.send(operator, last))
else
super
end
@ -53,13 +57,15 @@ module ActiveSupport
# The native Range#cover? behavior is untouched.
# ('a'..'f').cover?('c') # => true
# (5..9).cover?(11) # => false
#
# The given range must be fully bounded, with both start and end.
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? ? :< : :<=
value_max = !exclude_end? && value.exclude_end? ? value.max : value.last
super(value.first) && value_max.send(operator, last)
super(value.first) && (self.end.nil? || value_max.send(operator, last))
else
super
end

View File

@ -64,12 +64,28 @@ class RangeTest < ActiveSupport::TestCase
def test_include_with_endless_range
assert(eval("1..").include?(2))
end
def test_should_include_range_with_endless_range
assert(eval("1..").include?(2..4))
end
def test_should_not_include_range_with_endless_range
assert_not(eval("1..").include?(0..4))
end
end
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.7.0")
def test_include_with_beginless_range
assert(eval("..2").include?(1))
end
def test_should_include_range_with_beginless_range
assert(eval("..2").include?(-1..1))
end
def test_should_not_include_range_with_beginless_range
assert_not(eval("..2").include?(-1..3))
end
end
def test_should_compare_identical_inclusive
@ -84,6 +100,26 @@ class RangeTest < ActiveSupport::TestCase
assert((1..10) === (1...11))
end
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.6.0")
def test_should_compare_range_with_endless_range
assert(eval("1..") === (2..4))
end
def test_should_not_compare_range_with_endless_range
assert_not(eval("1..") === (0..4))
end
end
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.7.0")
def test_should_compare_range_with_beginless_range
assert(eval("..2") === (-1..1))
end
def test_should_not_compare_range_with_beginless_range
assert_not(eval("..2") === (-1..3))
end
end
def test_exclusive_end_should_not_include_identical_with_inclusive_end
assert_not_includes (1...10), 1..10
end
@ -109,6 +145,26 @@ class RangeTest < ActiveSupport::TestCase
assert((1..10).cover?(1...11))
end
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.6.0")
def test_should_cover_range_with_endless_range
assert(eval("1..").cover?(2..4))
end
def test_should_not_cover_range_with_endless_range
assert_not(eval("1..").cover?(0..4))
end
end
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.7.0")
def test_should_cover_range_with_beginless_range
assert(eval("..2").cover?(-1..1))
end
def test_should_not_cover_range_with_beginless_range
assert_not(eval("..2").cover?(-1..3))
end
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)