Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

root/trunk/activesupport/lib/active_support/duration.rb

Revision 8523, 2.6 kB (checked in by bitsweat, 8 months ago)

Ruby 1.9 compat: add #raise to AS::BasicObject, fixup Duration argument error. Closes #10594.

Line 
1 module ActiveSupport
2   # Provides accurate date and time measurements using Date#advance and
3   # Time#advance, respectively. It mainly supports the methods on Numeric,
4   # such as in this example:
5   #
6   #   1.month.ago       # equivalent to Time.now.advance(:months => -1)
7   class Duration < BasicObject
8     attr_accessor :value, :parts
9
10     def initialize(value, parts) #:nodoc:
11       @value, @parts = value, parts
12     end
13
14     # Adds another Duration or a Numeric to this Duration. Numeric values
15     # are treated as seconds.
16     def +(other)
17       if Duration === other
18         Duration.new(value + other.value, @parts + other.parts)
19       else
20         Duration.new(value + other, @parts + [[:seconds, other]])
21       end
22     end
23
24     # Subtracts another Duration or a Numeric from this Duration. Numeric
25     # values are treated as seconds.
26     def -(other)
27       self + (-other)
28     end
29
30     def -@ #:nodoc:
31       Duration.new(-value, parts.map { |type,number| [type, -number] })
32     end
33
34     def is_a?(klass) #:nodoc:
35       klass == Duration || super
36     end
37
38     # Returns true if <tt>other</tt> is also a Duration instance with the
39     # same <tt>value</tt>, or if <tt>other == value</tt>.
40     def ==(other)
41       if Duration === other
42         other.value == value
43       else
44         other == value
45       end
46     end
47
48     def self.===(other) #:nodoc:
49       other.is_a?(Duration) rescue super
50     end
51
52     # Calculates a new Time or Date that is as far in the future
53     # as this Duration represents.
54     def since(time = ::Time.now)
55       sum(1, time)
56     end
57     alias :from_now :since
58
59     # Calculates a new Time or Date that is as far in the past
60     # as this Duration represents.
61     def ago(time = ::Time.now)
62       sum(-1, time)
63     end
64     alias :until :ago
65
66     def inspect #:nodoc:
67       consolidated = parts.inject(::Hash.new(0)) { |h,part| h[part.first] += part.last; h }
68       [:years, :months, :days, :minutes, :seconds].map do |length|
69         n = consolidated[length]
70         "#{n} #{n == 1 ? length.to_s.singularize : length.to_s}" if n.nonzero?
71       end.compact.to_sentence
72     end
73
74     protected
75
76       def sum(sign, time = ::Time.now) #:nodoc:
77         parts.inject(time) do |t,(type,number)|
78           if t.acts_like?(:time) || t.acts_like?(:date)
79             if type == :seconds
80               t.since(sign * number)
81             else
82               t.advance(type => sign * number)
83             end
84           else
85             raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
86           end
87         end
88       end
89
90     private
91
92       def method_missing(method, *args, &block) #:nodoc:
93         value.send(method, *args)
94       end
95   end
96 end
Note: See TracBrowser for help on using the browser.