Froze rails gems
[depot.git] / vendor / rails / activesupport / lib / active_support / duration.rb
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.current)
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.current)
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.current) #: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