File: active_support/duration.rb

Overview
Module Structure
Class Hierarchy
Code

Overview

Module Structure

  module: <Toplevel Module>
  module: ActiveSupport#3
  class: Duration#9
inherits from
  BasicObject ( ActiveSupport )
has properties
attribute: value [RW] #10
attribute: parts [RW] #10
method: initialize / 2 #12
method: + / 1 #18
method: - / 1 #28
method: -@ #32
method: is_a? / 1 #36
method: == / 1 #42
class method: === / 1 #50
method: since / 1 #56
alias: from_now since #59
method: ago / 1 #63
alias: until ago #66
method: inspect #68
method: sum / 2 #80
method: method_missing / 3 #96

Class Hierarchy

Code

   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        parts = [: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
  74        parts = ["0 seconds"] if parts.empty?
  75        parts.to_sentence(:locale => :en)
  76      end
  77 
  78      protected
  79 
  80        def sum(sign, time = ::Time.current) #:nodoc:
  81          parts.inject(time) do |t,(type,number)|
  82            if t.acts_like?(:time) || t.acts_like?(:date)
  83              if type == :seconds
  84                t.since(sign * number)
  85              else
  86                t.advance(type => sign * number)
  87              end
  88            else
  89              raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
  90            end
  91          end
  92        end
  93 
  94      private
  95 
  96        def method_missing(method, *args, &block) #:nodoc:
  97          value.send(method, *args)
  98        end
  99    end
 100  end