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