class Spanner

Constants

ParseError
VERSION

Attributes

from[R]
raise_on_error[R]
value[R]

Public Class Methods

days_in_month(year, month) click to toggle source
# File lib/spanner.rb, line 30
def self.days_in_month(year, month)
  (Date.new(year, 12, 31) << (12 - month)).day
end
new(opts) click to toggle source
# File lib/spanner.rb, line 13
def initialize(opts)
  @value = value
  @on_error = opts && opts.key?(:on_error) ? opts[:on_error] : :raise
  @length_of_month = opts && opts[:length_of_month]

  @from = if opts && opts.key?(:from)
            case opts[:from]
            when :now
              Time.new.to_i
            else
              opts[:from].to_i
            end
          else
            0
          end
end
parse(str, opts = nil) click to toggle source
# File lib/spanner.rb, line 7
def self.parse(str, opts = nil)
  Spanner.new(opts).parse(str)
end

Public Instance Methods

error(err) click to toggle source
# File lib/spanner.rb, line 38
def error(err)
  if on_error == :raise
    raise ParseError.new(err)
  end
end
length_of_month() click to toggle source
# File lib/spanner.rb, line 34
def length_of_month
  @length_of_month ||= Spanner.parse("#{Spanner.days_in_month(Time.new.year, Time.new.month)} days")
end
parse(value) click to toggle source
# File lib/spanner.rb, line 44
def parse(value)
  parts = []
  part_contextualized = nil
  value.scan(/[\+\-]?(?:\d*\.\d+|\d+)|[a-z]+/i).each do |part|
    part_as_float = Float(part) rescue nil
    if part_as_float
      parts << part_as_float
      part_contextualized = nil
    else
      if part_contextualized
        error "Part has already been contextualized with #{part_contextualized}"
        return nil
      end

      if parts.empty?
        parts << 1
      end

      # part is context
      multiplier = case part
                   when "s", "sec", "seconds" then 1
                   when "m", "min", "minutes" then 60
                   when "h", "hours", "hrs" then 3600
                   when "d", "days" then 86_400
                   when "w", "wks", "weeks" then 604_800
                   when "months", "month", "M" then length_of_month
                   when "years", "y" then 31_556_926
                   when /\As/ then 1
                   when /\Am/ then 60
                   when /\Ah/ then 3600
                   when /\Ad/ then 86_400
                   when /\Aw/ then 604_800
                   when /\AM/ then length_of_month
                   when /\Ay/ then 31_556_926
                   end

      part_contextualized = part
      parts << (parts.pop * multiplier)
    end
  end

  if parts.empty?
    nil
  else
    value = parts.inject(from) { |s, p| s += p }
    value.ceil == value ? value.ceil : value
  end
end