activesupport 1.4.4 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activesupport might be problematic. Click here for more details.

Files changed (126) hide show
  1. data/CHANGELOG +263 -7
  2. data/lib/active_support.rb +9 -4
  3. data/lib/active_support/basic_object.rb +5 -0
  4. data/lib/active_support/buffered_logger.rb +107 -0
  5. data/lib/active_support/clean_logger.rb +94 -5
  6. data/lib/active_support/core_ext.rb +4 -1
  7. data/lib/active_support/core_ext/array.rb +8 -2
  8. data/lib/active_support/core_ext/array/access.rb +28 -0
  9. data/lib/active_support/core_ext/array/conversions.rb +28 -15
  10. data/lib/active_support/core_ext/array/extract_options.rb +19 -0
  11. data/lib/active_support/core_ext/array/grouping.rb +20 -7
  12. data/lib/active_support/core_ext/array/random_access.rb +12 -0
  13. data/lib/active_support/core_ext/bigdecimal.rb +1 -2
  14. data/lib/active_support/core_ext/bigdecimal/{formatting.rb → conversions.rb} +1 -2
  15. data/lib/active_support/core_ext/blank.rb +2 -8
  16. data/lib/active_support/core_ext/cgi.rb +2 -2
  17. data/lib/active_support/core_ext/class.rb +4 -3
  18. data/lib/active_support/core_ext/class/attribute_accessors.rb +1 -1
  19. data/lib/active_support/core_ext/class/delegating_attributes.rb +40 -0
  20. data/lib/active_support/core_ext/class/inheritable_attributes.rb +3 -3
  21. data/lib/active_support/core_ext/class/removal.rb +2 -2
  22. data/lib/active_support/core_ext/date.rb +5 -1
  23. data/lib/active_support/core_ext/date/behavior.rb +13 -0
  24. data/lib/active_support/core_ext/date/calculations.rb +188 -0
  25. data/lib/active_support/core_ext/date/conversions.rb +69 -13
  26. data/lib/active_support/core_ext/date_time.rb +10 -0
  27. data/lib/active_support/core_ext/date_time/calculations.rb +77 -0
  28. data/lib/active_support/core_ext/date_time/conversions.rb +54 -0
  29. data/lib/active_support/core_ext/duplicable.rb +37 -0
  30. data/lib/active_support/core_ext/enumerable.rb +1 -0
  31. data/lib/active_support/core_ext/exception.rb +2 -2
  32. data/lib/active_support/core_ext/file.rb +21 -0
  33. data/lib/active_support/core_ext/float.rb +5 -0
  34. data/lib/active_support/core_ext/float/rounding.rb +24 -0
  35. data/lib/active_support/core_ext/hash.rb +5 -5
  36. data/lib/active_support/core_ext/hash/conversions.rb +86 -34
  37. data/lib/active_support/core_ext/hash/diff.rb +8 -0
  38. data/lib/active_support/core_ext/hash/except.rb +24 -0
  39. data/lib/active_support/core_ext/hash/indifferent_access.rb +15 -2
  40. data/lib/active_support/core_ext/hash/keys.rb +10 -3
  41. data/lib/active_support/core_ext/hash/reverse_merge.rb +2 -2
  42. data/lib/active_support/core_ext/hash/slice.rb +28 -0
  43. data/lib/active_support/core_ext/integer.rb +2 -2
  44. data/lib/active_support/core_ext/kernel.rb +5 -4
  45. data/lib/active_support/core_ext/kernel/debugger.rb +13 -0
  46. data/lib/active_support/core_ext/module.rb +8 -7
  47. data/lib/active_support/core_ext/module/aliasing.rb +17 -5
  48. data/lib/active_support/core_ext/module/attr_accessor_with_default.rb +31 -0
  49. data/lib/active_support/core_ext/module/attribute_accessors.rb +1 -1
  50. data/lib/active_support/core_ext/module/delegation.rb +21 -0
  51. data/lib/active_support/core_ext/name_error.rb +2 -2
  52. data/lib/active_support/core_ext/numeric.rb +2 -2
  53. data/lib/active_support/core_ext/numeric/time.rb +30 -11
  54. data/lib/active_support/core_ext/object.rb +3 -2
  55. data/lib/active_support/core_ext/object/extending.rb +40 -29
  56. data/lib/active_support/core_ext/object/instance_variables.rb +22 -0
  57. data/lib/active_support/core_ext/object/misc.rb +29 -4
  58. data/lib/active_support/core_ext/pathname.rb +1 -1
  59. data/lib/active_support/core_ext/range.rb +7 -1
  60. data/lib/active_support/core_ext/range/blockless_step.rb +22 -0
  61. data/lib/active_support/core_ext/range/conversions.rb +8 -6
  62. data/lib/active_support/core_ext/range/include_range.rb +22 -0
  63. data/lib/active_support/core_ext/range/overlaps.rb +12 -0
  64. data/lib/active_support/core_ext/string.rb +10 -7
  65. data/lib/active_support/core_ext/string/conversions.rb +5 -1
  66. data/lib/active_support/core_ext/string/unicode.rb +2 -2
  67. data/lib/active_support/core_ext/string/xchar.rb +11 -0
  68. data/lib/active_support/core_ext/symbol.rb +12 -10
  69. data/lib/active_support/core_ext/test.rb +1 -0
  70. data/lib/active_support/core_ext/test/unit/assertions.rb +62 -0
  71. data/lib/active_support/core_ext/time.rb +4 -2
  72. data/lib/active_support/core_ext/time/behavior.rb +13 -0
  73. data/lib/active_support/core_ext/time/calculations.rb +87 -54
  74. data/lib/active_support/core_ext/time/conversions.rb +71 -10
  75. data/lib/active_support/dependencies.rb +25 -24
  76. data/lib/active_support/deprecation.rb +4 -2
  77. data/lib/active_support/duration.rb +86 -0
  78. data/lib/active_support/inflections.rb +2 -1
  79. data/lib/active_support/inflector.rb +13 -6
  80. data/lib/active_support/json.rb +22 -39
  81. data/lib/active_support/json/decoding.rb +60 -0
  82. data/lib/active_support/json/encoders/date.rb +5 -0
  83. data/lib/active_support/json/encoders/date_time.rb +5 -0
  84. data/lib/active_support/json/encoders/enumerable.rb +12 -0
  85. data/lib/active_support/json/encoders/false_class.rb +5 -0
  86. data/lib/active_support/json/encoders/hash.rb +50 -0
  87. data/lib/active_support/json/encoders/nil_class.rb +5 -0
  88. data/lib/active_support/json/encoders/numeric.rb +5 -0
  89. data/lib/active_support/json/encoders/object.rb +6 -0
  90. data/lib/active_support/json/encoders/regexp.rb +5 -0
  91. data/lib/active_support/json/encoders/string.rb +30 -0
  92. data/lib/active_support/json/encoders/symbol.rb +5 -0
  93. data/lib/active_support/json/encoders/time.rb +5 -0
  94. data/lib/active_support/json/encoders/true_class.rb +5 -0
  95. data/lib/active_support/json/encoding.rb +38 -0
  96. data/lib/active_support/json/variable.rb +10 -0
  97. data/lib/active_support/multibyte.rb +7 -5
  98. data/lib/active_support/multibyte/chars.rb +6 -0
  99. data/lib/active_support/multibyte/handlers/utf8_handler.rb +115 -5
  100. data/lib/active_support/option_merger.rb +7 -7
  101. data/lib/active_support/ordered_options.rb +22 -17
  102. data/lib/active_support/test_case.rb +5 -0
  103. data/lib/active_support/testing.rb +1 -0
  104. data/lib/active_support/testing/default.rb +12 -0
  105. data/lib/active_support/values/time_zone.rb +3 -3
  106. data/lib/active_support/vendor.rb +14 -0
  107. data/lib/active_support/vendor/builder-2.1.2/blankslate.rb +113 -0
  108. data/lib/active_support/vendor/{builder.rb → builder-2.1.2/builder.rb} +0 -0
  109. data/lib/active_support/vendor/builder-2.1.2/builder/blankslate.rb +20 -0
  110. data/lib/active_support/vendor/builder-2.1.2/builder/css.rb +250 -0
  111. data/lib/active_support/vendor/{builder → builder-2.1.2/builder}/xchar.rb +11 -8
  112. data/lib/active_support/vendor/{builder → builder-2.1.2/builder}/xmlbase.rb +38 -44
  113. data/lib/active_support/vendor/{builder → builder-2.1.2/builder}/xmlevents.rb +1 -1
  114. data/lib/active_support/vendor/{builder → builder-2.1.2/builder}/xmlmarkup.rb +40 -39
  115. data/lib/active_support/vendor/{xml_simple.rb → xml-simple-1.0.11/xmlsimple.rb} +3 -3
  116. data/lib/active_support/version.rb +3 -3
  117. data/lib/active_support/whiny_nil.rb +12 -12
  118. data/lib/activesupport.rb +1 -0
  119. metadata +69 -17
  120. data/lib/active_support/binding_of_caller.rb +0 -84
  121. data/lib/active_support/breakpoint.rb +0 -528
  122. data/lib/active_support/caching_tools.rb +0 -62
  123. data/lib/active_support/json/encoders.rb +0 -25
  124. data/lib/active_support/json/encoders/core.rb +0 -70
  125. data/lib/active_support/reloadable.rb +0 -60
  126. data/lib/active_support/vendor/builder/blankslate.rb +0 -63
@@ -1,5 +1,11 @@
1
- require File.dirname(__FILE__) + '/range/conversions'
1
+ require 'active_support/core_ext/range/conversions'
2
+ require 'active_support/core_ext/range/overlaps'
3
+ require 'active_support/core_ext/range/include_range'
4
+ require 'active_support/core_ext/range/blockless_step'
2
5
 
3
6
  class Range #:nodoc:
4
7
  include ActiveSupport::CoreExtensions::Range::Conversions
8
+ include ActiveSupport::CoreExtensions::Range::Overlaps
9
+ include ActiveSupport::CoreExtensions::Range::IncludeRange
10
+ include ActiveSupport::CoreExtensions::Range::BlocklessStep
5
11
  end
@@ -0,0 +1,22 @@
1
+ module ActiveSupport #:nodoc:
2
+ module CoreExtensions #:nodoc:
3
+ module Range #:nodoc:
4
+ # Return an array when step is called without a block.
5
+ module BlocklessStep
6
+ def self.included(base) #:nodoc:
7
+ base.alias_method_chain :step, :blockless
8
+ end
9
+
10
+ def step_with_blockless(value, &block)
11
+ if block_given?
12
+ step_without_blockless(value, &block)
13
+ else
14
+ returning [] do |array|
15
+ step_without_blockless(value) { |step| array << step }
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -6,16 +6,18 @@ module ActiveSupport #:nodoc:
6
6
  DATE_FORMATS = {
7
7
  :db => Proc.new { |start, stop| "BETWEEN '#{start.to_s(:db)}' AND '#{stop.to_s(:db)}'" }
8
8
  }
9
-
10
- def self.included(klass) #:nodoc:
11
- klass.send(:alias_method, :to_default_s, :to_s)
12
- klass.send(:alias_method, :to_s, :to_formatted_s)
9
+
10
+ def self.included(base) #:nodoc:
11
+ base.class_eval do
12
+ alias_method :to_default_s, :to_s
13
+ alias_method :to_s, :to_formatted_s
14
+ end
13
15
  end
14
-
16
+
15
17
  def to_formatted_s(format = :default)
16
18
  DATE_FORMATS[format] ? DATE_FORMATS[format].call(first, last) : to_default_s
17
19
  end
18
20
  end
19
21
  end
20
22
  end
21
- end
23
+ end
@@ -0,0 +1,22 @@
1
+ module ActiveSupport #:nodoc:
2
+ module CoreExtensions #:nodoc:
3
+ module Range #:nodoc:
4
+ # Check if a Range includes another Range.
5
+ module IncludeRange
6
+ def self.included(base) #:nodoc:
7
+ base.alias_method_chain :include?, :range
8
+ end
9
+
10
+ def include_with_range?(value)
11
+ if value.is_a?(::Range)
12
+ operator = exclude_end? ? :< : :<=
13
+ end_value = value.exclude_end? ? last.succ : last
14
+ include?(value.first) && (value.last <=> end_value).send(operator, 0)
15
+ else
16
+ include_without_range?(value)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,12 @@
1
+ module ActiveSupport #:nodoc:
2
+ module CoreExtensions #:nodoc:
3
+ module Range #:nodoc:
4
+ # Check if Ranges overlap.
5
+ module Overlaps
6
+ def overlaps?(other)
7
+ include?(other.first) || other.include?(first)
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -1,15 +1,18 @@
1
- require File.dirname(__FILE__) + '/string/inflections'
2
- require File.dirname(__FILE__) + '/string/conversions'
3
- require File.dirname(__FILE__) + '/string/access'
4
- require File.dirname(__FILE__) + '/string/starts_ends_with'
5
- require File.dirname(__FILE__) + '/string/iterators'
6
- require File.dirname(__FILE__) + '/string/unicode'
1
+ require 'active_support/core_ext/string/inflections'
2
+ require 'active_support/core_ext/string/conversions'
3
+ require 'active_support/core_ext/string/access'
4
+ require 'active_support/core_ext/string/starts_ends_with'
5
+ require 'active_support/core_ext/string/iterators' unless 'test'.respond_to?(:each_char)
6
+ require 'active_support/core_ext/string/unicode'
7
+ require 'active_support/core_ext/string/xchar'
7
8
 
8
9
  class String #:nodoc:
9
10
  include ActiveSupport::CoreExtensions::String::Access
10
11
  include ActiveSupport::CoreExtensions::String::Conversions
11
12
  include ActiveSupport::CoreExtensions::String::Inflections
12
13
  include ActiveSupport::CoreExtensions::String::StartsEndsWith
13
- include ActiveSupport::CoreExtensions::String::Iterators
14
+ if defined? ActiveSupport::CoreExtensions::String::Iterators
15
+ include ActiveSupport::CoreExtensions::String::Iterators
16
+ end
14
17
  include ActiveSupport::CoreExtensions::String::Unicode
15
18
  end
@@ -7,12 +7,16 @@ module ActiveSupport #:nodoc:
7
7
  module Conversions
8
8
  # Form can be either :utc (default) or :local.
9
9
  def to_time(form = :utc)
10
- ::Time.send(form, *ParseDate.parsedate(self))
10
+ ::Time.send("#{form}_time", *ParseDate.parsedate(self)[0..5].map {|arg| arg || 0})
11
11
  end
12
12
 
13
13
  def to_date
14
14
  ::Date.new(*ParseDate.parsedate(self)[0..2])
15
15
  end
16
+
17
+ def to_datetime
18
+ ::DateTime.civil(*ParseDate.parsedate(self)[0..5].map {|arg| arg || 0} << 0)
19
+ end
16
20
  end
17
21
  end
18
22
  end
@@ -1,7 +1,7 @@
1
1
  module ActiveSupport #:nodoc:
2
2
  module CoreExtensions #:nodoc:
3
3
  module String #:nodoc:
4
- # Define methods for handeling unicode data.
4
+ # Define methods for handling unicode data.
5
5
  module Unicode
6
6
  # +chars+ is a Unicode safe proxy for string methods. It creates and returns an instance of the
7
7
  # ActiveSupport::Multibyte::Chars class which encapsulates the original string. A Unicode safe version of all
@@ -39,4 +39,4 @@ module ActiveSupport #:nodoc:
39
39
  end
40
40
  end
41
41
  end
42
- end
42
+ end
@@ -0,0 +1,11 @@
1
+ begin
2
+ # See http://bogomips.org/fast_xs/ by Eric Wong
3
+ require 'fast_xs'
4
+
5
+ class String
6
+ alias_method :original_xs, :to_xs if method_defined?(:to_xs)
7
+ alias_method :to_xs, :fast_xs
8
+ end
9
+ rescue LoadError
10
+ # fast_xs extension unavailable.
11
+ end
@@ -1,12 +1,14 @@
1
- class Symbol
2
- # Turns the symbol into a simple proc, which is especially useful for enumerations. Examples:
3
- #
4
- # # The same as people.collect { |p| p.name }
5
- # people.collect(&:name)
6
- #
7
- # # The same as people.select { |p| p.manager? }.collect { |p| p.salary }
8
- # people.select(&:manager?).collect(&:salary)
9
- def to_proc
10
- Proc.new { |*args| args.shift.__send__(self, *args) }
1
+ unless :test.respond_to?(:to_proc)
2
+ class Symbol
3
+ # Turns the symbol into a simple proc, which is especially useful for enumerations. Examples:
4
+ #
5
+ # # The same as people.collect { |p| p.name }
6
+ # people.collect(&:name)
7
+ #
8
+ # # The same as people.select { |p| p.manager? }.collect { |p| p.salary }
9
+ # people.select(&:manager?).collect(&:salary)
10
+ def to_proc
11
+ Proc.new { |*args| args.shift.__send__(self, *args) }
12
+ end
11
13
  end
12
14
  end
@@ -0,0 +1 @@
1
+ require 'active_support/core_ext/test/unit/assertions'
@@ -0,0 +1,62 @@
1
+ module Test #:nodoc:
2
+ module Unit #:nodoc:
3
+ # FIXME: no Proc#binding in Ruby 2, must change this API
4
+ module Assertions #:nodoc:
5
+ # Test numeric difference between the return value of an expression as a result of what is evaluated
6
+ # in the yielded block.
7
+ #
8
+ # assert_difference 'Article.count' do
9
+ # post :create, :article => {...}
10
+ # end
11
+ #
12
+ # An arbitrary expression is passed in and evaluated.
13
+ #
14
+ # assert_difference 'assigns(:article).comments(:reload).size' do
15
+ # post :create, :comment => {...}
16
+ # end
17
+ #
18
+ # An arbitrary positive or negative difference can be specified. The default is +1.
19
+ #
20
+ # assert_difference 'Article.count', -1 do
21
+ # post :delete, :id => ...
22
+ # end
23
+ #
24
+ # An array of expressions can also be passed in and evaluated.
25
+ #
26
+ # assert_difference [ 'Article.count', 'Post.count' ], +2 do
27
+ # post :create, :article => {...}
28
+ # end
29
+ #
30
+ # A error message can be specified.
31
+ #
32
+ # assert_difference 'Article.count', -1, "An Article should be destroyed" do
33
+ # post :delete, :id => ...
34
+ # end
35
+ def assert_difference(expressions, difference = 1, message = nil, &block)
36
+ expression_evaluations = Array(expressions).collect{ |expression| lambda { eval(expression, block.send!(:binding)) } }
37
+
38
+ original_values = expression_evaluations.inject([]) { |memo, expression| memo << expression.call }
39
+ yield
40
+ expression_evaluations.each_with_index do |expression, i|
41
+ assert_equal original_values[i] + difference, expression.call, message
42
+ end
43
+ end
44
+
45
+ # Assertion that the numeric result of evaluating an expression is not changed before and after
46
+ # invoking the passed in block.
47
+ #
48
+ # assert_no_difference 'Article.count' do
49
+ # post :create, :article => invalid_attributes
50
+ # end
51
+ #
52
+ # A error message can be specified.
53
+ #
54
+ # assert_no_difference 'Article.count', "An Article should not be destroyed" do
55
+ # post :create, :article => invalid_attributes
56
+ # end
57
+ def assert_no_difference(expressions, message = nil, &block)
58
+ assert_difference expressions, 0, message, &block
59
+ end
60
+ end
61
+ end
62
+ end
@@ -8,10 +8,12 @@ class Time
8
8
  end
9
9
  end
10
10
 
11
- require File.dirname(__FILE__) + '/time/calculations'
12
- require File.dirname(__FILE__) + '/time/conversions'
11
+ require 'active_support/core_ext/time/behavior'
12
+ require 'active_support/core_ext/time/calculations'
13
+ require 'active_support/core_ext/time/conversions'
13
14
 
14
15
  class Time#:nodoc:
16
+ include ActiveSupport::CoreExtensions::Time::Behavior
15
17
  include ActiveSupport::CoreExtensions::Time::Calculations
16
18
  include ActiveSupport::CoreExtensions::Time::Conversions
17
19
  end
@@ -0,0 +1,13 @@
1
+ module ActiveSupport #:nodoc:
2
+ module CoreExtensions #:nodoc:
3
+ module Time #:nodoc:
4
+ module Behavior
5
+ # Enable more predictable duck-typing on Time-like classes. See
6
+ # Object#acts_like?.
7
+ def acts_like_time?
8
+ true
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -4,7 +4,14 @@ module ActiveSupport #:nodoc:
4
4
  # Enables the use of time calculations within Time itself
5
5
  module Calculations
6
6
  def self.included(base) #:nodoc:
7
- base.extend(ClassMethods)
7
+ base.extend ClassMethods
8
+
9
+ base.class_eval do
10
+ alias_method :plus_without_duration, :+
11
+ alias_method :+, :plus_with_duration
12
+ alias_method :minus_without_duration, :-
13
+ alias_method :-, :minus_with_duration
14
+ end
8
15
  end
9
16
 
10
17
  module ClassMethods
@@ -21,36 +28,56 @@ module ActiveSupport #:nodoc:
21
28
  month % 2 == 0 ? 31 : 30
22
29
  end
23
30
  end
31
+
32
+ # Returns a new Time if requested year can be accommodated by Ruby's Time class
33
+ # (i.e., if year is within either 1970..2038 or 1902..2038, depending on system architecture);
34
+ # otherwise returns a DateTime
35
+ def time_with_datetime_fallback(utc_or_local, year, month=1, day=1, hour=0, min=0, sec=0, usec=0)
36
+ ::Time.send(utc_or_local, year, month, day, hour, min, sec, usec)
37
+ rescue
38
+ offset = utc_or_local.to_sym == :local ? ::DateTime.local_offset : 0
39
+ ::DateTime.civil(year, month, day, hour, min, sec, offset)
40
+ end
41
+
42
+ # wraps class method time_with_datetime_fallback with utc_or_local == :utc
43
+ def utc_time(*args)
44
+ time_with_datetime_fallback(:utc, *args)
45
+ end
46
+
47
+ # wraps class method time_with_datetime_fallback with utc_or_local == :local
48
+ def local_time(*args)
49
+ time_with_datetime_fallback(:local, *args)
50
+ end
24
51
  end
25
52
 
26
53
  # Seconds since midnight: Time.now.seconds_since_midnight
27
54
  def seconds_since_midnight
28
55
  self.to_i - self.change(:hour => 0).to_i + (self.usec/1.0e+6)
29
56
  end
30
-
57
+
31
58
  # Returns a new Time where one or more of the elements have been changed according to the +options+ parameter. The time options
32
- # (hour, minute, sec, usec) reset cascadingly, so if only the hour is passed, then minute, sec, and usec is set to 0. If the hour and
59
+ # (hour, minute, sec, usec) reset cascadingly, so if only the hour is passed, then minute, sec, and usec is set to 0. If the hour and
33
60
  # minute is passed, then sec and usec is set to 0.
34
61
  def change(options)
35
62
  ::Time.send(
36
- self.utc? ? :utc : :local,
37
- options[:year] || self.year,
38
- options[:month] || self.month,
39
- options[:mday] || self.mday,
40
- options[:hour] || self.hour,
63
+ self.utc? ? :utc_time : :local_time,
64
+ options[:year] || self.year,
65
+ options[:month] || self.month,
66
+ options[:day] || self.day,
67
+ options[:hour] || self.hour,
41
68
  options[:min] || (options[:hour] ? 0 : self.min),
42
69
  options[:sec] || ((options[:hour] || options[:min]) ? 0 : self.sec),
43
70
  options[:usec] || ((options[:hour] || options[:min] || options[:sec]) ? 0 : self.usec)
44
71
  )
45
72
  end
46
-
47
- # Uses Date to provide precise Time calculations for years, months, and days. The +options+ parameter takes a hash with
48
- # any of these keys: :months, :days, :years.
73
+
74
+ # Uses Date to provide precise Time calculations for years, months, and days. The +options+ parameter takes a hash with
75
+ # any of these keys: :years, :months, :weeks, :days, :hours, :minutes, :seconds.
49
76
  def advance(options)
50
- d = ::Date.new(year + (options.delete(:years) || 0), month, day)
51
- d = d >> options.delete(:months) if options[:months]
52
- d = d + options.delete(:days) if options[:days]
53
- change(options.merge(:year => d.year, :month => d.month, :mday => d.day))
77
+ d = to_date.advance(options)
78
+ time_advanced_by_date = change(:year => d.year, :month => d.month, :day => d.day)
79
+ seconds_to_advance = (options[:seconds] || 0) + (options[:minutes] || 0) * 60 + (options[:hours] || 0) * 3600
80
+ seconds_to_advance == 0 ? time_advanced_by_date : time_advanced_by_date.since(seconds_to_advance)
54
81
  end
55
82
 
56
83
  # Returns a new Time representing the time a number of seconds ago, this is basically a wrapper around the Numeric extension
@@ -59,58 +86,43 @@ module ActiveSupport #:nodoc:
59
86
  self.since(-seconds)
60
87
  end
61
88
 
62
- # Returns a new Time representing the time a number of seconds since the instance time, this is basically a wrapper around
63
- #the Numeric extension. Do not use this method in combination with x.months, use months_since instead!
89
+ # Returns a new Time representing the time a number of seconds since the instance time, this is basically a wrapper around
90
+ # the Numeric extension. Do not use this method in combination with x.months, use months_since instead!
64
91
  def since(seconds)
65
92
  initial_dst = self.dst? ? 1 : 0
66
93
  f = seconds.since(self)
67
94
  final_dst = f.dst? ? 1 : 0
68
95
  (seconds.abs >= 86400 && initial_dst != final_dst) ? f + (initial_dst - final_dst).hours : f
96
+ rescue
97
+ self.to_datetime.since(seconds)
69
98
  end
70
99
  alias :in :since
71
100
 
72
101
  # Returns a new Time representing the time a number of specified months ago
73
102
  def months_ago(months)
74
- months_since(-months)
103
+ advance(:months => -months)
75
104
  end
76
105
 
106
+ # Returns a new Time representing the time a number of specified months in the future
77
107
  def months_since(months)
78
- year, month, mday = self.year, self.month, self.mday
79
-
80
- month += months
81
-
82
- # in case months is negative
83
- while month < 1
84
- month += 12
85
- year -= 1
86
- end
87
-
88
- # in case months is positive
89
- while month > 12
90
- month -= 12
91
- year += 1
92
- end
93
-
94
- max = ::Time.days_in_month(month, year)
95
- mday = max if mday > max
96
-
97
- change(:year => year, :month => month, :mday => mday)
108
+ advance(:months => months)
98
109
  end
99
110
 
100
111
  # Returns a new Time representing the time a number of specified years ago
101
112
  def years_ago(years)
102
- change(:year => self.year - years)
113
+ advance(:years => -years)
103
114
  end
104
-
115
+
116
+ # Returns a new Time representing the time a number of specified years in the future
105
117
  def years_since(years)
106
- change(:year => self.year + years)
118
+ advance(:years => years)
107
119
  end
108
120
 
109
121
  # Short-hand for years_ago(1)
110
122
  def last_year
111
123
  years_ago(1)
112
124
  end
113
-
125
+
114
126
  # Short-hand for years_since(1)
115
127
  def next_year
116
128
  years_since(1)
@@ -126,7 +138,7 @@ module ActiveSupport #:nodoc:
126
138
  def next_month
127
139
  months_since(1)
128
140
  end
129
-
141
+
130
142
  # Returns a new Time representing the "start" of this week (Monday, 0:00)
131
143
  def beginning_of_week
132
144
  days_to_monday = self.wday!=0 ? self.wday-1 : 6
@@ -134,13 +146,13 @@ module ActiveSupport #:nodoc:
134
146
  end
135
147
  alias :monday :beginning_of_week
136
148
  alias :at_beginning_of_week :beginning_of_week
137
-
149
+
138
150
  # Returns a new Time representing the start of the given day in next week (default is Monday).
139
151
  def next_week(day = :monday)
140
152
  days_into_week = { :monday => 0, :tuesday => 1, :wednesday => 2, :thursday => 3, :friday => 4, :saturday => 5, :sunday => 6}
141
153
  since(1.week).beginning_of_week.since(days_into_week[day].day).change(:hour => 0)
142
154
  end
143
-
155
+
144
156
  # Returns a new Time representing the start of the day (0:00)
145
157
  def beginning_of_day
146
158
  (self - self.seconds_since_midnight).change(:usec => 0)
@@ -148,43 +160,64 @@ module ActiveSupport #:nodoc:
148
160
  alias :midnight :beginning_of_day
149
161
  alias :at_midnight :beginning_of_day
150
162
  alias :at_beginning_of_day :beginning_of_day
151
-
163
+
164
+ # Returns a new Time representing the end of the day (23:59:59)
165
+ def end_of_day
166
+ change(:hour => 23, :min => 59, :sec => 59)
167
+ end
168
+
152
169
  # Returns a new Time representing the start of the month (1st of the month, 0:00)
153
170
  def beginning_of_month
154
171
  #self - ((self.mday-1).days + self.seconds_since_midnight)
155
- change(:mday => 1,:hour => 0, :min => 0, :sec => 0, :usec => 0)
172
+ change(:day => 1,:hour => 0, :min => 0, :sec => 0, :usec => 0)
156
173
  end
157
174
  alias :at_beginning_of_month :beginning_of_month
158
-
175
+
159
176
  # Returns a new Time representing the end of the month (last day of the month, 0:00)
160
177
  def end_of_month
161
178
  #self - ((self.mday-1).days + self.seconds_since_midnight)
162
179
  last_day = ::Time.days_in_month( self.month, self.year )
163
- change(:mday => last_day,:hour => 0, :min => 0, :sec => 0, :usec => 0)
180
+ change(:day => last_day, :hour => 23, :min => 59, :sec => 59, :usec => 0)
164
181
  end
165
182
  alias :at_end_of_month :end_of_month
166
-
183
+
167
184
  # Returns a new Time representing the start of the quarter (1st of january, april, july, october, 0:00)
168
185
  def beginning_of_quarter
169
186
  beginning_of_month.change(:month => [10, 7, 4, 1].detect { |m| m <= self.month })
170
187
  end
171
188
  alias :at_beginning_of_quarter :beginning_of_quarter
172
-
189
+
173
190
  # Returns a new Time representing the start of the year (1st of january, 0:00)
174
191
  def beginning_of_year
175
- change(:month => 1,:mday => 1,:hour => 0, :min => 0, :sec => 0, :usec => 0)
192
+ change(:month => 1,:day => 1,:hour => 0, :min => 0, :sec => 0, :usec => 0)
176
193
  end
177
194
  alias :at_beginning_of_year :beginning_of_year
178
-
195
+
179
196
  # Convenience method which returns a new Time representing the time 1 day ago
180
197
  def yesterday
181
198
  self.ago(1.day)
182
199
  end
183
-
200
+
184
201
  # Convenience method which returns a new Time representing the time 1 day since the instance time
185
202
  def tomorrow
186
203
  self.since(1.day)
187
204
  end
205
+
206
+ def plus_with_duration(other) #:nodoc:
207
+ if ActiveSupport::Duration === other
208
+ other.since(self)
209
+ else
210
+ plus_without_duration(other)
211
+ end
212
+ end
213
+
214
+ def minus_with_duration(other) #:nodoc:
215
+ if ActiveSupport::Duration === other
216
+ other.until(self)
217
+ else
218
+ minus_without_duration(other)
219
+ end
220
+ end
188
221
  end
189
222
  end
190
223
  end