diff options
17 files changed, 199 insertions, 7 deletions
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 6917be7efc..bee93ebfbc 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,5 +1,18 @@ ## Rails 4.0.0 (unreleased) ## +* It's now possible to compare Date, DateTime, Time and TimeWithZone with Infinity + This allows to create date/time ranges with one infinite bound. + Example: + + range = Range.new(Date.today, Float::INFINITY) + + Also it's possible to check inclusion of date/time in range with conversion. + + range.include?(Time.now + 1.year) # => true + range.include?(DateTime.now + 1.year) # => true + + *Alexander Grebennik* + * Remove meaningless `ActiveSupport::FrozenObjectError`, which was just an alias of `RuntimeError`. *Akira Matsuda* diff --git a/activesupport/lib/active_support/core_ext/date.rb b/activesupport/lib/active_support/core_ext/date.rb index 465fedda80..5f13f5f70f 100644 --- a/activesupport/lib/active_support/core_ext/date.rb +++ b/activesupport/lib/active_support/core_ext/date.rb @@ -2,4 +2,5 @@ require 'active_support/core_ext/date/acts_like' require 'active_support/core_ext/date/calculations' require 'active_support/core_ext/date/conversions' require 'active_support/core_ext/date/zones' +require 'active_support/core_ext/date/infinite_comparable' diff --git a/activesupport/lib/active_support/core_ext/date/infinite_comparable.rb b/activesupport/lib/active_support/core_ext/date/infinite_comparable.rb new file mode 100644 index 0000000000..ca5d793942 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/date/infinite_comparable.rb @@ -0,0 +1,5 @@ +require 'active_support/core_ext/infinite_comparable' + +class Date + include InfiniteComparable +end diff --git a/activesupport/lib/active_support/core_ext/date_time.rb b/activesupport/lib/active_support/core_ext/date_time.rb index e8a27b9f38..024af91738 100644 --- a/activesupport/lib/active_support/core_ext/date_time.rb +++ b/activesupport/lib/active_support/core_ext/date_time.rb @@ -2,3 +2,4 @@ require 'active_support/core_ext/date_time/acts_like' require 'active_support/core_ext/date_time/calculations' require 'active_support/core_ext/date_time/conversions' require 'active_support/core_ext/date_time/zones' +require 'active_support/core_ext/date_time/infinite_comparable' diff --git a/activesupport/lib/active_support/core_ext/date_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_time/calculations.rb index fca5d4d679..4e4852a5e6 100644 --- a/activesupport/lib/active_support/core_ext/date_time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date_time/calculations.rb @@ -142,11 +142,4 @@ class DateTime def utc_offset (offset * 86400).to_i end - - # Layers additional behavior on DateTime#<=> so that Time and - # ActiveSupport::TimeWithZone instances can be compared with a DateTime. - def <=>(other) - super other.to_datetime - end - end diff --git a/activesupport/lib/active_support/core_ext/date_time/infinite_comparable.rb b/activesupport/lib/active_support/core_ext/date_time/infinite_comparable.rb new file mode 100644 index 0000000000..8a282b19f2 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/date_time/infinite_comparable.rb @@ -0,0 +1,5 @@ +require 'active_support/core_ext/infinite_comparable' + +class DateTime + include InfiniteComparable +end diff --git a/activesupport/lib/active_support/core_ext/infinite_comparable.rb b/activesupport/lib/active_support/core_ext/infinite_comparable.rb new file mode 100644 index 0000000000..a69ff6a37b --- /dev/null +++ b/activesupport/lib/active_support/core_ext/infinite_comparable.rb @@ -0,0 +1,30 @@ +require 'active_support/concern' + +module InfiniteComparable + extend ActiveSupport::Concern + + included do + alias_method_chain :<=>, :infinity + end + + define_method '<=>_with_infinity' do |other| + if other.class == self.class + self.send(:'<=>_without_infinity', other) + # inf <=> inf + elsif other.respond_to?(:infinite?) && other.infinite? && respond_to?(:infinite?) && infinite? + infinite? <=> other.infinite? + # not_inf <=> inf + elsif other.respond_to?(:infinite?) && other.infinite? + -other.infinite? + # inf <=> not_inf + elsif respond_to?(:infinite?) && infinite? + infinite? + else + conversion = :"to_#{self.class.name.downcase}" + + other = other.send(conversion) if other.respond_to?(conversion) + + self.send(:'<=>_without_infinity', other) + end + end +end diff --git a/activesupport/lib/active_support/core_ext/numeric.rb b/activesupport/lib/active_support/core_ext/numeric.rb index a6bc0624be..d5cfc2ece4 100644 --- a/activesupport/lib/active_support/core_ext/numeric.rb +++ b/activesupport/lib/active_support/core_ext/numeric.rb @@ -1,3 +1,4 @@ require 'active_support/core_ext/numeric/bytes' require 'active_support/core_ext/numeric/time' require 'active_support/core_ext/numeric/conversions' +require 'active_support/core_ext/numeric/infinite_comparable' diff --git a/activesupport/lib/active_support/core_ext/numeric/infinite_comparable.rb b/activesupport/lib/active_support/core_ext/numeric/infinite_comparable.rb new file mode 100644 index 0000000000..07d4d7446a --- /dev/null +++ b/activesupport/lib/active_support/core_ext/numeric/infinite_comparable.rb @@ -0,0 +1,11 @@ +require 'active_support/core_ext/big_decimal/conversions' +require 'active_support/number_helper' +require 'active_support/core_ext/infinite_comparable' + +class Float + include InfiniteComparable +end + +class BigDecimal + include InfiniteComparable +end diff --git a/activesupport/lib/active_support/core_ext/time.rb b/activesupport/lib/active_support/core_ext/time.rb index 32cffe237d..af6b589b71 100644 --- a/activesupport/lib/active_support/core_ext/time.rb +++ b/activesupport/lib/active_support/core_ext/time.rb @@ -3,3 +3,4 @@ require 'active_support/core_ext/time/calculations' require 'active_support/core_ext/time/conversions' require 'active_support/core_ext/time/marshal' require 'active_support/core_ext/time/zones' +require 'active_support/core_ext/time/infinite_comparable' diff --git a/activesupport/lib/active_support/core_ext/time/infinite_comparable.rb b/activesupport/lib/active_support/core_ext/time/infinite_comparable.rb new file mode 100644 index 0000000000..63795885f5 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/time/infinite_comparable.rb @@ -0,0 +1,5 @@ +require 'active_support/core_ext/infinite_comparable' + +class Time + include InfiniteComparable +end diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb index 5e89a6e00b..f8b219fee6 100644 --- a/activesupport/test/core_ext/date_ext_test.rb +++ b/activesupport/test/core_ext/date_ext_test.rb @@ -353,6 +353,13 @@ class DateExtBehaviorTest < ActiveSupport::TestCase Date.today.freeze.freeze end end + + def test_compare_with_infinity + assert_nothing_raised do + assert_equal(-1, Date.today <=> Float::INFINITY) + assert_equal(1, Date.today <=> -Float::INFINITY) + end + end end class DateExtConversionsTest < ActiveSupport::TestCase diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb index dfad3a90f8..1bcec7aac4 100644 --- a/activesupport/test/core_ext/date_time_ext_test.rb +++ b/activesupport/test/core_ext/date_time_ext_test.rb @@ -317,3 +317,12 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ') end end + +class DateTimeExtBehaviorTest < ActiveSupport::TestCase + def test_compare_with_infinity + assert_nothing_raised do + assert_equal(-1, DateTime.now <=> Float::INFINITY) + assert_equal(1, DateTime.now <=> -Float::INFINITY) + end + end +end diff --git a/activesupport/test/core_ext/numeric_ext_test.rb b/activesupport/test/core_ext/numeric_ext_test.rb index 435f4aa5a1..5839aa40e5 100644 --- a/activesupport/test/core_ext/numeric_ext_test.rb +++ b/activesupport/test/core_ext/numeric_ext_test.rb @@ -447,3 +447,67 @@ class NumericExtFormattingTest < ActiveSupport::TestCase assert_equal '1 Million', BigDecimal("1000010").to_s(:human) end end + +class NumericExtBehaviorTest < ActiveSupport::TestCase + def setup + @inf = BigDecimal.new('Infinity') + end + + def test_compare_infinity_with_date + assert_nothing_raised do + assert_equal(-1, -Float::INFINITY <=> Date.today) + assert_equal(1, Float::INFINITY <=> Date.today) + assert_equal(-1, -@inf <=> Date.today) + assert_equal(1, @inf <=> Date.today) + end + end + + def test_compare_infinty_with_infinty + assert_nothing_raised do + assert_equal(-1, -Float::INFINITY <=> Float::INFINITY) + assert_equal(1, Float::INFINITY <=> -Float::INFINITY) + assert_equal(0, Float::INFINITY <=> Float::INFINITY) + assert_equal(0, -Float::INFINITY <=> -Float::INFINITY) + + assert_equal(-1, -Float::INFINITY <=> BigDecimal::INFINITY) + assert_equal(1, Float::INFINITY <=> -BigDecimal::INFINITY) + assert_equal(0, Float::INFINITY <=> BigDecimal::INFINITY) + assert_equal(0, -Float::INFINITY <=> -BigDecimal::INFINITY) + + assert_equal(-1, -BigDecimal::INFINITY <=> Float::INFINITY) + assert_equal(1, BigDecimal::INFINITY <=> -Float::INFINITY) + assert_equal(0, BigDecimal::INFINITY <=> Float::INFINITY) + assert_equal(0, -BigDecimal::INFINITY <=> -Float::INFINITY) + end + end + + def test_compare_infinity_with_time + assert_nothing_raised do + assert_equal(-1, -Float::INFINITY <=> Time.now) + assert_equal(1, Float::INFINITY <=> Time.now) + assert_equal(-1, -@inf <=> Time.now) + assert_equal(1, @inf <=> Time.now) + end + end + + def test_compare_infinity_with_datetime + assert_nothing_raised do + assert_equal(-1, -Float::INFINITY <=> DateTime.now) + assert_equal(1, Float::INFINITY <=> DateTime.now) + assert_equal(-1, -@inf <=> DateTime.now) + assert_equal(1, @inf <=> DateTime.now) + end + end + + def test_compare_infinity_with_twz + time_zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] + twz = ActiveSupport::TimeWithZone.new(Time.now, time_zone) + + assert_nothing_raised do + assert_equal(-1, -Float::INFINITY <=> twz) + assert_equal(1, Float::INFINITY <=> twz) + assert_equal(-1, -@inf <=> twz) + assert_equal(1, @inf <=> twz) + end + end +end diff --git a/activesupport/test/core_ext/range_ext_test.rb b/activesupport/test/core_ext/range_ext_test.rb index f0cdc0bfd4..0051c48984 100644 --- a/activesupport/test/core_ext/range_ext_test.rb +++ b/activesupport/test/core_ext/range_ext_test.rb @@ -1,6 +1,7 @@ require 'abstract_unit' require 'active_support/time' require 'active_support/core_ext/range' +require 'active_support/core_ext/numeric' class RangeTest < ActiveSupport::TestCase def test_to_s_from_dates @@ -85,4 +86,28 @@ class RangeTest < ActiveSupport::TestCase time_range_2 = Time.utc(2005, 12, 10, 17, 31)..Time.utc(2005, 12, 10, 18, 00) assert !time_range_1.overlaps?(time_range_2) end + + def test_infinite_bounds + time_zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] + + time = Time.now + date = Date.today + datetime = DateTime.now + twz = ActiveSupport::TimeWithZone.new(time, time_zone) + + infinity1 = Float::INFINITY + infinity2 = BigDecimal.new('Infinity') + + [infinity1, infinity2].each do |infinity| + [time, date, datetime, twz].each do |bound| + [time, date, datetime, twz].each do |value| + assert Range.new(bound, infinity).include?(value + 10.years) + assert Range.new(-infinity, bound).include?(value - 10.years) + + assert !Range.new(bound, infinity).include?(value - 10.years) + assert !Range.new(-infinity, bound).include?(value + 10.years) + end + end + end + end end diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb index 70e07cefd1..1c75530084 100644 --- a/activesupport/test/core_ext/time_ext_test.rb +++ b/activesupport/test/core_ext/time_ext_test.rb @@ -843,3 +843,12 @@ class TimeExtMarshalingTest < ActiveSupport::TestCase assert_equal Time.local(2004, 2, 29), Time.local(2004, 5, 31).last_quarter end end + +class TimeExtBehaviorTest < ActiveSupport::TestCase + def test_compare_with_infinity + assert_nothing_raised do + assert_equal(-1, Time.now <=> Float::INFINITY) + assert_equal(1, Time.now <=> -Float::INFINITY) + end + end +end diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb index 56020da035..76dc6a35c8 100644 --- a/activesupport/test/core_ext/time_with_zone_test.rb +++ b/activesupport/test/core_ext/time_with_zone_test.rb @@ -1081,3 +1081,15 @@ class TimeWithZoneMethodsForString < ActiveSupport::TestCase Time.zone = old_tz end end + +class TimeWithZoneExtBehaviorTest < ActiveSupport::TestCase + def test_compare_with_infinity + time_zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] + twz = ActiveSupport::TimeWithZone.new(Time.now, time_zone) + + assert_nothing_raised do + assert_equal(-1, twz <=> Float::INFINITY) + assert_equal(1, twz <=> -Float::INFINITY) + end + end +end |