class IniFile::Parser

The IniFile::Parser has the responsibility of reading the contents of an .ini file and storing that information into a ruby Hash. The object being parsed must respond to `each_line` - this includes Strings and any IO object.

Attributes

property[RW]
section[W]
value[RW]

Public Class Methods

new( hash, param, comment, default, continuation, force_array ) click to toggle source

Create a new IniFile::Parser that can be used to parse the contents of an .ini file.

hash - The Hash where parsed information will be stored param - String used to separate parameter and value comment - String containing the comment character(s) default - The String name of the default global section continuation - Use backslash as a line continuation character

# File lib/inifile.rb, line 455
def initialize( hash, param, comment, default, continuation, force_array )
  @hash = hash
  @default = default
  @continuation = continuation
  @force_array = force_array

  comment = comment.to_s.empty? ? "\\z" : "\\s*(?:[#{comment}].*)?\\z"

  @section_regexp  = %r/\A\s*\[([^\]]+)\]#{comment}/
  @ignore_regexp   = %r/\A#{comment}/
  @property_regexp = %r/\A(.*?)(?<!\\)#{param}(.*)\z/
  @switch_regexp   = %r/\A\s*([\w\.]+)\s*#{comment}/

  @open_quote      = %r/\A\s*(".*)\z/
  @close_quote     = %r/\A(.*(?<!\\)")#{comment}/
  @full_quote      = %r/\A\s*(".*(?<!\\)")#{comment}/
  if @continuation
    @trailing_slash = %r/\A(.*)(?<!\\)\\#{comment}/
  else
    @trailing_slash = %r/\A(.*\\)#{comment}/
  end
  @normal_value    = %r/\A(.*?)#{comment}/
end

Public Instance Methods

error( msg = 'Could not parse line' ) click to toggle source

Raise a parse error using the given message and appending the current line being parsed.

msg - The message String to use.

Raises IniFile::Error

# File lib/inifile.rb, line 625
def error( msg = 'Could not parse line' )
  raise Error, "#{msg}: #{@line.inspect}"
end
leading_quote?() click to toggle source

Returns `true` if the current value starts with a leading double quote. Otherwise returns false.

# File lib/inifile.rb, line 481
def leading_quote?
  value && value =~ %r/\A"/
end
parse( content ) click to toggle source

Parse the ini file contents. This will clear any values currently stored in the ini hash.

content - Any object that responds to `each_line`

Returns nil.

# File lib/inifile.rb, line 546
def parse( content )
  return unless content

  continuation = false

  @hash.clear
  @line = nil
  self.section = nil

  content.each_line do |line|
    @line = line.chomp

    if continuation
      continuation = parse_value @line
    else
      case @line
      when @ignore_regexp
        nil
      when @section_regexp
        self.section = @hash[$1]
      when @property_regexp
        self.property = $1.strip
        error if property.empty?

        continuation = parse_value $2
      when @switch_regexp
        #self.property = $1.strip
        self.section[$1.strip] = {}
      else
        error
      end
    end
  end
  # check here if we have a dangling value ... usually means we have an
  # unmatched open quote
  if leading_quote?
    error "Unmatched open quote"
  elsif property && value
    process_property
  elsif value
    error
  end

  nil
end
parse_value( string ) click to toggle source

Given a string, attempt to parse out a value from that string. This value might be continued on the following line. So this method returns `true` if it is expecting more data.

string - String to parse

Returns `true` if the next line is also part of the current value. Returns `fase` if the string contained a complete value.

# File lib/inifile.rb, line 493
def parse_value( string )
  continuation = false

  # if our value starts with a double quote, then we are in a
  # line continuation situation
  if leading_quote?
    # check for a closing quote at the end of the string
    if string =~ @close_quote
      value << $1

    # otherwise just append the string to the value
    else
      value << string
      continuation = true
    end

  # not currently processing a continuation line
  else
    case string
    when @full_quote
      self.value = $1

    when @open_quote
      self.value = $1
      continuation = true

    when @trailing_slash
      self.value ?  self.value << $1 : self.value = $1
      continuation = @continuation

    when @normal_value
      self.value ?  self.value << $1 : self.value = $1

    else
      error
    end
  end

  if continuation
    self.value << $/ if leading_quote?
  else
    process_property
  end

  continuation
end
process_property() click to toggle source

Store the property/value pair in the currently active section. This method checks for continuation of the value to the next line.

Returns nil.

# File lib/inifile.rb, line 596
def process_property
  property.strip!
  value.strip!

  self.value = $1 if value =~ %r/\A"(.*)(?<!\\)"\z/m

  if section[property].nil? || !@force_array
    section[property] = typecast(value)
  elsif section[property].is_a?(Array)
    section[property] << typecast(value)
  else
    section[property] = [section[property], typecast(value)]
  end

  self.property = nil
  self.value = nil
end
section() click to toggle source

Returns the current section Hash.

# File lib/inifile.rb, line 615
def section
  @section ||= @hash[@default]
end
typecast( value ) click to toggle source

Attempt to typecast the value string. We are looking for boolean values, integers, floats, and empty strings. Below is how each gets cast, but it is pretty logical and straightforward.

"true"  -->  true
"false" -->  false
""      -->  nil
"42"    -->  42
"3.14"  -->  3.14
"foo"   -->  "foo"

Returns the typecast value.

# File lib/inifile.rb, line 641
def typecast( value )
  case value
  when %r/\Atrue\z/i;  true
  when %r/\Afalse\z/i; false
  when %r/\A\s*\z/i;   nil
  else
    stripped_value = value.strip
    if stripped_value =~ /^\d*\.\d+$/
      Float(stripped_value)
    elsif stripped_value =~ /^[^0]\d*$/
      Integer(stripped_value)
    else
      unescape_value(value)
    end
  end
rescue
  unescape_value(value)
end
unescape_value( value ) click to toggle source

Unescape special characters found in the value string. This will convert escaped null, tab, carriage return, newline, and backslash into their literal equivalents.

value - The String value to unescape.

Returns the unescaped value.

# File lib/inifile.rb, line 667
def unescape_value( value )
  value = value.to_s
  value.gsub!(%r/\\[0nrt\\]/) { |char|
    case char
    when '\0';   "\0"
    when '\n';   "\n"
    when '\r';   "\r"
    when '\t';   "\t"
    when '\\\\'; "\\"
    end
  }
  value
end