123456789_123456789_123456789_123456789_123456789_

Class: ActiveSupport::Duration::ISO8601Parser

Do not use. This class is for internal use only.
Relationships & Source Files
Namespace Children
Exceptions:
Inherits: Object
Defined in: activesupport/lib/active_support/duration/iso8601_parser.rb

Overview

Parses a string formatted according to ISO 8601 ::ActiveSupport::Duration into the hash.

See ISO 8601 for more information.

This parser allows negative parts to be present in pattern.

Constant Summary

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(string) ⇒ ISO8601Parser

[ GitHub ]

  
# File 'activesupport/lib/active_support/duration/iso8601_parser.rb', line 34

def initialize(string)
  @scanner = StringScanner.new(string)
  @parts = {}
  @mode = :start
  @sign = 1
end

Instance Attribute Details

#finished?Boolean (readonly, private)

[ GitHub ]

  
# File 'activesupport/lib/active_support/duration/iso8601_parser.rb', line 83

def finished?
  scanner.eos?
end

#mode (rw)

[ GitHub ]

  
# File 'activesupport/lib/active_support/duration/iso8601_parser.rb', line 32

attr_accessor :mode, :sign

#parts (readonly)

[ GitHub ]

  
# File 'activesupport/lib/active_support/duration/iso8601_parser.rb', line 31

attr_reader :parts, :scanner

#scanner (readonly)

[ GitHub ]

  
# File 'activesupport/lib/active_support/duration/iso8601_parser.rb', line 31

attr_reader :parts, :scanner

#sign (rw)

[ GitHub ]

  
# File 'activesupport/lib/active_support/duration/iso8601_parser.rb', line 32

attr_accessor :mode, :sign

Instance Method Details

#number (private)

Parses number which can be a float with either comma or period.

[ GitHub ]

  
# File 'activesupport/lib/active_support/duration/iso8601_parser.rb', line 88

def number
  PERIOD_OR_COMMA.match?(scanner[1]) ? scanner[1].tr(COMMA, PERIOD).to_f : scanner[1].to_i
end

#parse!

[ GitHub ]

  
# File 'activesupport/lib/active_support/duration/iso8601_parser.rb', line 41

def parse!
  while !finished?
    case mode
    when :start
      if scan(SIGN_MARKER)
        self.sign = (scanner.matched == "-") ? -1 : 1
        self.mode = :sign
      else
        raise_parsing_error
      end

    when :sign
      if scan(DATE_MARKER)
        self.mode = :date
      else
        raise_parsing_error
      end

    when :date
      if scan(TIME_MARKER)
        self.mode = :time
      elsif scan(DATE_COMPONENT)
        parts[DATE_TO_PART[scanner[2]]] = number * sign
      else
        raise_parsing_error
      end

    when :time
      if scan(TIME_COMPONENT)
        parts[TIME_TO_PART[scanner[2]]] = number * sign
      else
        raise_parsing_error
      end

    end
  end

  validate!
  parts
end

#raise_parsing_error(reason = nil) (private)

Raises:

[ GitHub ]

  
# File 'activesupport/lib/active_support/duration/iso8601_parser.rb', line 96

def raise_parsing_error(reason = nil)
  raise ParsingError, "Invalid ISO 8601 duration: #{scanner.string.inspect} #{reason}".strip
end

#scan(pattern) (private)

[ GitHub ]

  
# File 'activesupport/lib/active_support/duration/iso8601_parser.rb', line 92

def scan(pattern)
  scanner.scan(pattern)
end

#validate! (private)

Checks for various semantic errors as stated in ISO 8601 standard.

[ GitHub ]

  
# File 'activesupport/lib/active_support/duration/iso8601_parser.rb', line 101

def validate!
  raise_parsing_error("is empty duration") if parts.empty?

  # Mixing any of Y, M, D with W is invalid.
  if parts.key?(:weeks) && (parts.keys & DATE_COMPONENTS).any?
    raise_parsing_error("mixing weeks with other date parts not allowed")
  end

  # Specifying an empty T part is invalid.
  if mode == :time && (parts.keys & TIME_COMPONENTS).empty?
    raise_parsing_error("time part marker is present but time part is empty")
  end

  fractions = parts.values.reject(&:zero?).select { |a| (a % 1) != 0 }
  unless fractions.empty? || (fractions.size == 1 && fractions.last == @parts.values.reject(&:zero?).last)
    raise_parsing_error "(only last part can be fractional)"
  end

  true
end