Hello community, here is the log from the commit of package rubygem-inifile for openSUSE:Factory checked in at 2015-02-11 16:45:01 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/rubygem-inifile (Old) and /work/SRC/openSUSE:Factory/.rubygem-inifile.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rubygem-inifile" Changes: -------- --- /work/SRC/openSUSE:Factory/rubygem-inifile/rubygem-inifile.changes 2012-09-23 21:13:34.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.rubygem-inifile.new/rubygem-inifile.changes 2015-02-11 16:45:06.000000000 +0100 @@ -1,0 +2,5 @@ +Tue Feb 10 17:56:17 UTC 2015 - [email protected] + +- updated to version 3.0.0 + +------------------------------------------------------------------- Old: ---- inifile-2.0.2.gem New: ---- gem2rpm.yml inifile-3.0.0.gem ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ rubygem-inifile.spec ++++++ --- /var/tmp/diff_new_pack.k2WwzR/_old 2015-02-11 16:45:07.000000000 +0100 +++ /var/tmp/diff_new_pack.k2WwzR/_new 2015-02-11 16:45:07.000000000 +0100 @@ -1,7 +1,7 @@ # # spec file for package rubygem-inifile # -# Copyright (c) 2012 SUSE LINUX Products GmbH, Nuernberg, Germany. +# Copyright (c) 2015 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -16,17 +16,26 @@ # +# +# This file was generated with a gem2rpm.yml and not just plain gem2rpm. +# All sections marked as MANUAL, license headers, summaries and descriptions +# can be maintained in that file. Please consult this file before editing any +# of those fields +# + Name: rubygem-inifile -Version: 2.0.2 +Version: 3.0.0 Release: 0 %define mod_name inifile %define mod_full_name %{mod_name}-%{version} - BuildRoot: %{_tmppath}/%{name}-%{version}-build -BuildRequires: ruby-macros >= 1 -BuildRequires: rubygem(rdoc) > 3.10 +BuildRequires: %{rubygem gem2rpm} +BuildRequires: %{rubygem rdoc > 3.10} +BuildRequires: %{ruby} +BuildRequires: ruby-macros >= 5 Url: http://rubygems.org/gems/inifile -Source: %{mod_full_name}.gem +Source: http://rubygems.org/gems/%{mod_full_name}.gem +Source1: gem2rpm.yml Summary: INI file reader and writer License: MIT Group: Development/Languages/Ruby @@ -62,48 +71,17 @@ [section2] # another comment var1 = baz -var2 = shoodle - -%package doc -Summary: RDoc documentation for %{mod_name} -Group: Development/Languages/Ruby -Requires: %{name} = %{version} - -%description doc -Documentation generated at gem installation time. -Usually in RDoc and RI formats. - -%package testsuite -Summary: Test suite for %{mod_name} -Group: Development/Languages/Ruby -Requires: %{name} = %{version} - -%description testsuite -Test::Unit or RSpec files, useful for developers. +var2 = shoodle. %prep -#gem_unpack -#if you need patches, apply them here and replace the # with a % sign in the surrounding lines -#gem_build %build %install -%gem_install -f +%gem_install \ + --doc-files="History.txt README.md" \ + -f -%files -%defattr(-,root,root,-) -%{_libdir}/ruby/gems/%{rb_ver}/cache/%{mod_full_name}.gem -%{_libdir}/ruby/gems/%{rb_ver}/gems/%{mod_full_name}/ -%exclude %{_libdir}/ruby/gems/%{rb_ver}/gems/%{mod_full_name}/test -%{_libdir}/ruby/gems/%{rb_ver}/specifications/%{mod_full_name}.gemspec - -%files doc -%defattr(-,root,root,-) -%doc %{_libdir}/ruby/gems/%{rb_ver}/doc/%{mod_full_name}/ - -%files testsuite -%defattr(-,root,root,-) -%{_libdir}/ruby/gems/%{rb_ver}/gems/%{mod_full_name}/test +%gem_packages %changelog ++++++ gem2rpm.yml ++++++ # --- # ## used by gem2rpm # :summary: this is a custom summary # ## used by gem2rpm # :description: |- # this is a custom description # # it can be multiline # ## used by gem2rpm # :license: MIT or Ruby # ## used by gem2rpm and gem_packages # :version_suffix: -x_y # ## used by gem2rpm and gem_packages # :disable_docs: true # ## used by gem2rpm # :disable_automatic_rdoc_dep: true # ## used by gem2rpm # :preamble: |- # BuildRequires: foobar # Requires: foobar # ## used by gem2rpm # :patches: # foo.patch: -p1 # bar.patch: # ## used by gem2rpm :sources: # - foo.desktop # - bar.desktop # :gem_install_args: '....' # ## used by gem2rpm # :pre_install: |- # %if 0%{?use_system_libev} # export USE_VENDORED_LIBEV="no" # %endif # ## used by gem2rpm # :post_install: |- # # delete custom files here or do other fancy stuff # install -D -m 0644 %{S:1} %{buildroot}%{_bindir}/gem2rpm-opensuse # ## used by gem2rpm # :testsuite_command: |- # (pushd %{buildroot}%{gem_base}/gems/%{mod_full_name} && rake test) # ## used by gem2rpm # :filelist: |- # /usr/bin/gem2rpm-opensuse # ## used by gem2rpm # :scripts: # :post: |- # /bin/echo foo # ## used by gem_packages # :main: # :preamble: |- # Requires: util-linux # Recommends: pwgen # :filelist: |- # /usr/bin/gem2rpm-opensuse # ## used by gem_packages # :custom: # apache: # :preamble: |- # Requires: ..... # :filelist: |- # /etc/apache2/conf.d/passenger.conf # :summary: Custom summary is optional # :description: |- # Custom description is optional # # bar # :post: |- # /bin/echo foo # ++++++ inifile-2.0.2.gem -> inifile-3.0.0.gem ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/.gitignore new/.gitignore --- old/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ new/.gitignore 2014-08-01 18:30:01.000000000 +0200 @@ -15,3 +15,4 @@ coverage doc pkg +vendor diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/README.md new/README.md --- old/README.md 1970-01-01 01:00:00.000000000 +0100 +++ new/README.md 2014-08-01 18:30:01.000000000 +0200 @@ -135,6 +135,19 @@ property = this is not a tab \\t character +### Value Type Casting + +Some values will be type cast when parsed by the code. Those values are +booleans, integers, floats, and empty strings are cast to `nil`. + +* "" --> nil +* "42" --> 42 +* "3.14159" --> 3.14159 +* "true" --> true +* "false" --> false +* "normal string" --> "normal string" + +Pretty basic stuff. Install ------- @@ -150,6 +163,15 @@ $ rake +Examples +-------- + + require 'inifile' + myini = IniFile.load('mytest.ini') + myini.each_section do |section| + puts "I want #{myini[section]['somevar']} printed here!" + end + Contributing ------------ @@ -171,7 +193,7 @@ ------- MIT License -Copyright (c) 2006 - 2012 +Copyright (c) 2006 - 2014 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Rakefile new/Rakefile --- old/Rakefile 1970-01-01 01:00:00.000000000 +0100 +++ new/Rakefile 2014-08-01 18:30:01.000000000 +0200 @@ -20,5 +20,5 @@ version IniFile::VERSION use_gmail - depend_on 'bones-git', :development => true + depend_on 'bones-git', "~> 1.3", :development => true } Files old/checksums.yaml.gz and new/checksums.yaml.gz differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/inifile.gemspec new/inifile.gemspec --- old/inifile.gemspec 1970-01-01 01:00:00.000000000 +0100 +++ new/inifile.gemspec 2014-08-01 18:30:01.000000000 +0200 @@ -0,0 +1,24 @@ +# coding: utf-8 +lib = File.expand_path('../lib', __FILE__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +require 'inifile' + +Gem::Specification.new do |spec| + spec.name = "inifile" + spec.version = IniFile::VERSION + spec.authors = ["Tim Pease"] + spec.email = ["[email protected]"] + spec.summary = %q{INI file reader and writer} + spec.description = %q{INI file reader and writer} + spec.description = %q{IniFile is a pure ruby gem that can read and write most INI file formats} + spec.homepage = "https://rubygems.org/gems/inifile" + spec.license = "MIT" + + spec.files = `git ls-files -z`.split("\x0") + spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } + spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) + spec.require_paths = ["lib"] + + spec.add_development_dependency "bones", "~> 3.8" + spec.add_development_dependency "bones-git", "~> 1.3" +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/inifile.rb new/lib/inifile.rb --- old/lib/inifile.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/lib/inifile.rb 2014-08-01 18:30:01.000000000 +0200 @@ -1,23 +1,20 @@ #encoding: UTF-8 -require 'strscan' # This class represents the INI file and can be used to parse, modify, # and write INI files. -# class IniFile include Enumerable class Error < StandardError; end - VERSION = '2.0.2' + VERSION = '3.0.0' # Public: Open an INI file and load the contents. # - # filename - The name of the fiel as a String + # filename - The name of the file as a String # opts - The Hash of options (default: {}) # :comment - String containing the comment character(s) # :parameter - String used to separate parameter and value - # :encoding - Encoding String for reading / writing (Ruby 1.9) - # :escape - Boolean used to control character escaping + # :encoding - Encoding String for reading / writing # :default - The String name of the default global section # # Examples @@ -28,8 +25,7 @@ # IniFile.load('does/not/exist.ini') # #=> nil # - # Returns an IniFile intsnace or nil if the file could not be opened. - # + # Returns an IniFile instance or nil if the file could not be opened. def self.load( filename, opts = {} ) return unless File.file? filename new(opts.merge(:filename => filename)) @@ -38,57 +34,50 @@ # Get and set the filename attr_accessor :filename - # Get and set the encoding (Ruby 1.9) + # Get and set the encoding attr_accessor :encoding - # Enable or disable character escaping - attr_accessor :escape - - # Public: Create a new INI file from the given content String which - # contains the INI file lines. If the content are omitted, then the - # :filename option is used to read in the content of the INI file. If - # neither the content for a filename is provided then an empty INI file is - # created. - # - # content - The String containing the INI file contents - # opts - The Hash of options (default: {}) - # :comment - String containing the comment character(s) - # :parameter - String used to separate parameter and value - # :encoding - Encoding String for reading / writing (Ruby 1.9) - # :escape - Boolean used to control character escaping - # :default - The String name of the default global section - # :filename - The filename as a String + # Public: Create a new INI file from the given set of options. If :content + # is provided then it will be used to populate the INI file. If a :filename + # is provided then the contents of the file will be parsed and stored in the + # INI file. If neither the :content or :filename is provided then an empty + # INI file is created. + # + # opts - The Hash of options (default: {}) + # :content - The String/Hash containing the INI contents + # :comment - String containing the comment character(s) + # :parameter - String used to separate parameter and value + # :encoding - Encoding String for reading / writing + # :default - The String name of the default global section + # :filename - The filename as a String # # Examples # # IniFile.new # #=> an empty IniFile instance # - # IniFile.new( "[global]\nfoo=bar" ) + # IniFile.new( :content => "[global]\nfoo=bar" ) # #=> an IniFile instance # # IniFile.new( :filename => 'file.ini', :encoding => 'UTF-8' ) # #=> an IniFile instance # - # IniFile.new( "[global]\nfoo=bar", :comment => '#' ) + # IniFile.new( :content => "[global]\nfoo=bar", :comment => '#' ) # #=> an IniFile instance # - def initialize( content = nil, opts = {} ) - opts, content = content, nil if Hash === content - - @content = content - + def initialize( opts = {} ) @comment = opts.fetch(:comment, ';#') @param = opts.fetch(:parameter, '=') @encoding = opts.fetch(:encoding, nil) - @escape = opts.fetch(:escape, true) @default = opts.fetch(:default, 'global') @filename = opts.fetch(:filename, nil) + content = opts.fetch(:content, nil) @ini = Hash.new {|h,k| h[k] = Hash.new} - if @content then parse! - elsif @filename then read + if content.is_a?(Hash) then merge!(content) + elsif content then parse(content) + elsif @filename then read end end @@ -98,16 +87,13 @@ # # opts - The default options Hash # :filename - The filename as a String - # :encoding - The encoding as a String (Ruby 1.9) + # :encoding - The encoding as a String # # Returns this IniFile instance. - # def write( opts = {} ) filename = opts.fetch(:filename, @filename) encoding = opts.fetch(:encoding, @encoding) - mode = (RUBY_VERSION >= '1.9' && encoding) ? - "w:#{encoding.to_s}" : - 'w' + mode = encoding ? "w:#{encoding}" : "w" File.open(filename, mode) do |f| @ini.each do |section,hash| @@ -129,31 +115,22 @@ # # opts - The default options Hash # :filename - The filename as a String - # :encoding - The encoding as a String (Ruby 1.9) + # :encoding - The encoding as a String # # Returns this IniFile instance if the read was successful; nil is returned # if the file could not be read. - # def read( opts = {} ) filename = opts.fetch(:filename, @filename) encoding = opts.fetch(:encoding, @encoding) return unless File.file? filename - mode = (RUBY_VERSION >= '1.9' && encoding) ? - "r:#{encoding.to_s}" : - 'r' - fd = File.open(filename, mode) - @content = fd.read - - parse! + mode = encoding ? "r:#{encoding}" : "r" + File.open(filename, mode) { |fd| parse fd } self - ensure - fd.close if fd && !fd.closed? end alias :restore :read # Returns this IniFile converted to a String. - # def to_s s = [] @ini.each do |section,hash| @@ -165,7 +142,6 @@ end # Returns this IniFile converted to a Hash. - # def to_h @ini.dup end @@ -176,33 +152,50 @@ # other - The other IniFile. # # Returns a new IniFile. - # def merge( other ) self.dup.merge!(other) end # Public: Merges other_inifile into this inifile, overwriting existing - # entries. Useful for having a system inifile with user over-ridable settings + # entries. Useful for having a system inifile with user overridable settings # elsewhere. # # other - The other IniFile. # # Returns this IniFile. - # def merge!( other ) + return self if other.nil? + my_keys = @ini.keys - other_keys = - case other - when IniFile; other.instance_variable_get(:@ini).keys - when Hash; other.keys - else raise "cannot merge contents from '#{other.class.name}'" end + other_keys = case other + when IniFile + other.instance_variable_get(:@ini).keys + when Hash + other.keys + else + raise Error, "cannot merge contents from '#{other.class.name}'" + end (my_keys & other_keys).each do |key| - @ini[key].merge!(other[key]) + case other[key] + when Hash + @ini[key].merge!(other[key]) + when nil + nil + else + raise Error, "cannot merge section #{key.inspect} - unsupported type: #{other[key].class.name}" + end end (other_keys - my_keys).each do |key| - @ini[key] = other[key] + @ini[key] = case other[key] + when Hash + other[key].dup + when nil + {} + else + raise Error, "cannot merge section #{key.inspect} - unsupported type: #{other[key].class.name}" + end end self @@ -212,7 +205,7 @@ # given block. # # block - The block that will be iterated by the each method. The block will - # be passed the current section and the parameter / value pair. + # be passed the current section and the parameter/value pair. # # Examples # @@ -221,7 +214,6 @@ # end # # Returns this IniFile. - # def each return unless block_given? @ini.each do |section,hash| @@ -244,7 +236,6 @@ # end # # Returns this IniFile. - # def each_section return unless block_given? @ini.each_key {|section| yield section} @@ -256,7 +247,6 @@ # section - The section name as a String. # # Returns the deleted section Hash. - # def delete_section( section ) @ini.delete section.to_s end @@ -272,7 +262,6 @@ # #=> global section Hash # # Returns the Hash of parameter/value pairs for this section. - # def []( section ) return nil if section.nil? @ini[section.to_s] @@ -289,7 +278,6 @@ # #=> { 'gritty' => 'yes' } # # Returns the value Hash. - # def []=( section, value ) @ini[section.to_s] = value end @@ -306,7 +294,6 @@ # # Return a Hash containing only those sections that match the given regular # expression. - # def match( regex ) @ini.dup.delete_if { |section, _| section !~ regex } end @@ -316,13 +303,11 @@ # section - The section name as a String. # # Returns true if the section exists in the IniFile. - # def has_section?( section ) @ini.has_key? section.to_s end # Returns an Array of section names contained in this IniFile. - # def sections @ini.keys end @@ -331,7 +316,6 @@ # the object will raise an error. # # Returns this IniFile. - # def freeze super @ini.each_value {|h| h.freeze} @@ -343,7 +327,6 @@ # marking each as tainted. # # Returns this IniFile. - # def taint super @ini.each_value {|h| h.taint} @@ -356,7 +339,6 @@ # original. The tainted state of the original is copied to the duplicate. # # Returns a new IniFile. - # def dup other = super other.instance_variable_set(:@ini, Hash.new {|h,k| h[k] = Hash.new}) @@ -371,7 +353,6 @@ # to the duplicate. # # Returns a new IniFile. - # def clone other = dup other.freeze if self.frozen? @@ -385,7 +366,6 @@ # other - The other IniFile. # # Returns true if the INI files are equivalent and false if they differ. - # def eql?( other ) return true if equal? other return false unless other.instance_of? self.class @@ -393,159 +373,255 @@ end alias :== :eql? - -private - - # Parse the ini file contents. This will clear any values currently stored - # in the ini hash. + # Escape special characters. # - def parse! - return unless @content - - string = '' - property = '' - - @ini.clear - @_line = nil - @_section = nil + # value - The String value to escape. + # + # Returns the escaped value. + def escape_value( value ) + value = value.to_s.dup + value.gsub!(%r/\\([0nrt])/, '\\\\\1') + value.gsub!(%r/\n/, '\n') + value.gsub!(%r/\r/, '\r') + value.gsub!(%r/\t/, '\t') + value.gsub!(%r/\0/, '\0') + value + end - scanner = StringScanner.new(@content) - until scanner.eos? + # Parse the given content and store the information in this IniFile + # instance. All data will be cleared out and replaced with the information + # read from the content. + # + # content - A String or a file descriptor (must respond to `each_line`) + # + # Returns this IniFile. + def parse( content ) + parser = Parser.new(@ini, @param, @comment, @default) + parser.parse(content) + self + end - # keep track of the current line for error messages - @_line = scanner.check(%r/\A.*$/) if scanner.bol? + # 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. + class Parser + + attr_writer :section + attr_accessor :property + attr_accessor :value + + # 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 + # + def initialize( hash, param, comment, default ) + @hash = hash + @default = default + + 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/ + + @open_quote = %r/\A\s*(".*)\z/ + @close_quote = %r/\A(.*(?<!\\)")#{comment}/ + @full_quote = %r/\A\s*(".*(?<!\\)")#{comment}/ + @trailing_slash = %r/\A(.*)(?<!\\)\\#{comment}/ + @normal_value = %r/\A(.*?)#{comment}/ + end - # look for escaped special characters \# \" etc - if scanner.scan(%r/\\([\[\]#{@param}#{@comment}"])/) - string << scanner[1] + # Returns `true` if the current value starts with a leading double quote. + # Otherwise returns false. + def leading_quote? + value && value =~ %r/\A"/ + end - # look for quoted strings - elsif scanner.scan(%r/"/) - quote = scanner.scan_until(/(?:\A|[^\\])"/) - parse_error('Unmatched quote') if quote.nil? + # 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. + 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 - quote.chomp!('"') - string << quote + # otherwise just append the string to the value + else + value << string + continuation = true + end - # look for comments, empty strings, end of lines - elsif scanner.skip(%r/\A\s*(?:[#{@comment}].*)?$/) - string << scanner.getch unless scanner.eos? + # 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 = true - process_property(property, string) + when @normal_value + self.value ? self.value << $1 : self.value = $1 - # look for the separator between property name and value - elsif scanner.scan(%r/#{@param}/) - if property.empty? - property = string.strip - string.slice!(0, string.length) else - parse_error + error end + end - # look for the start of a new section - elsif scanner.scan(%r/\A\s*\[([^\]]+)\]/) - @_section = @ini[scanner[1]] - - # otherwise scan and store characters till we hit the start of some - # special section like a quote, newline, comment, etc. + if continuation + self.value << $/ if leading_quote? else - tmp = scanner.scan_until(%r/([\n"#{@param}#{@comment}] | \z | \\[\[\]#{@param}#{@comment}"])/mx) - parse_error if tmp.nil? - - len = scanner[1].length - tmp.slice!(tmp.length - len, len) - - scanner.pos = scanner.pos - len - string << tmp + process_property end + + continuation end - process_property(property, string) - end + # 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. + def parse( content ) + return unless content + + continuation = false + + @hash.clear + @line = nil + self.section = nil - # Store the property / value pair in the currently active section. This - # method checks for continuation of the value to the next line. - # - # property - The property name as a String. - # value - The property value as a String. - # - # Returns nil. - # - def process_property( property, value ) - value.chomp! - return if property.empty? and value.empty? - return if value.sub!(%r/\\\s*\z/, '') + content.each_line do |line| + @line = line.chomp - property.strip! - value.strip! + 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 + else + error + end + end + end - parse_error if property.empty? + # 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 - current_section[property.dup] = unescape_value(value.dup) + nil + end - property.slice!(0, property.length) - value.slice!(0, value.length) + # Store the property/value pair in the currently active section. This + # method checks for continuation of the value to the next line. + # + # Returns nil. + def process_property + property.strip! + value.strip! - nil - end + self.value = $1 if value =~ %r/\A"(.*)(?<!\\)"\z/m - # Returns the current section Hash. - # - def current_section - @_section ||= @ini[@default] - end + section[property] = typecast(value) - # Raise a parse error using the given message and appending the current line - # being parsed. - # - # msg - The message String to use. - # - # Raises IniFile::Error - # - def parse_error( msg = 'Could not parse line' ) - raise Error, "#{msg}: #{@_line.inspect}" - end + self.property = nil + self.value = nil + end - # 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. - # - def unescape_value( value ) - return value unless @escape + # Returns the current section Hash. + def section + @section ||= @hash[@default] + end - 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 + # Raise a parse error using the given message and appending the current line + # being parsed. + # + # msg - The message String to use. + # + # Raises IniFile::Error + def error( msg = 'Could not parse line' ) + raise Error, "#{msg}: #{@line.inspect}" + end - # Escape special characters. - # - # value - The String value to escape. - # - # Returns the escaped value. - # - def escape_value( value ) - return value unless @escape + # 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. + 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 + Integer(value) rescue \ + Float(value) rescue \ + unescape_value(value) + end + end - value = value.to_s.dup - value.gsub!(%r/\\([0nrt])/, '\\\\\1') - value.gsub!(%r/\n/, '\n') - value.gsub!(%r/\r/, '\r') - value.gsub!(%r/\t/, '\t') - value.gsub!(%r/\0/, '\0') - value + # 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. + 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 end end # IniFile diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/metadata new/metadata --- old/metadata 1970-01-01 01:00:00.000000000 +0100 +++ new/metadata 2014-08-01 18:30:01.000000000 +0200 @@ -1,80 +1,104 @@ --- !ruby/object:Gem::Specification name: inifile version: !ruby/object:Gem::Version - version: 2.0.2 - prerelease: + version: 3.0.0 platform: ruby authors: - Tim Pease autorequire: bindir: bin cert_chain: [] -date: 2012-09-15 00:00:00.000000000 Z +date: 2014-08-01 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: bones-git requirement: !ruby/object:Gem::Requirement - none: false requirements: - - - ! '>=' + - - "~>" - !ruby/object:Gem::Version - version: '0' + version: '1.3' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement - none: false requirements: - - - ! '>=' + - - "~>" - !ruby/object:Gem::Version - version: '0' + version: '1.3' - !ruby/object:Gem::Dependency name: bones requirement: !ruby/object:Gem::Requirement - none: false requirements: - - - ! '>=' + - - ">=" - !ruby/object:Gem::Version - version: 3.8.0 + version: 3.8.1 type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement - none: false requirements: - - - ! '>=' + - - ">=" - !ruby/object:Gem::Version - version: 3.8.0 -description: ! "Although made popular by Windows, INI files can be used on any system - thanks\nto their flexibility. They allow a program to store configuration data, - which\ncan then be easily parsed and changed. Two notable systems that use the INI\nformat - are Samba and Trac.\n\nMore information about INI files can be found on the [Wikipedia - Page](http://en.wikipedia.org/wiki/INI_file).\n\n### Properties\n\nThe basic element - contained in an INI file is the property. Every property has\na name and a value, - delimited by an equals sign *=*. The name appears to the\nleft of the equals sign - and the value to the right.\n\n name=value\n\n### Sections\n\nSection declarations - start with *[* and end with *]* as in `[section1]` and\n`[section2]` shown in the - example below. The section declaration marks the\nbeginning of a section. All properties - after the section declaration will be\nassociated with that section.\n\n### Comments\n\nAll - lines beginning with a semicolon *;* or a number sign *#* are considered\nto be - comments. Comment lines are ignored when parsing INI files.\n\n### Example File - Format\n\nA typical INI file might look like this:\n\n [section1]\n ; some - comment on section1\n var1 = foo\n var2 = doodle\n var3 = multiline values - \\\n are also possible\n\n [section2]\n # another comment\n var1 = baz\n - \ var2 = shoodle" + version: 3.8.1 +description: |- + Although made popular by Windows, INI files can be used on any system thanks + to their flexibility. They allow a program to store configuration data, which + can then be easily parsed and changed. Two notable systems that use the INI + format are Samba and Trac. + + More information about INI files can be found on the [Wikipedia Page](http://en.wikipedia.org/wiki/INI_file). + + ### Properties + + The basic element contained in an INI file is the property. Every property has + a name and a value, delimited by an equals sign *=*. The name appears to the + left of the equals sign and the value to the right. + + name=value + + ### Sections + + Section declarations start with *[* and end with *]* as in `[section1]` and + `[section2]` shown in the example below. The section declaration marks the + beginning of a section. All properties after the section declaration will be + associated with that section. + + ### Comments + + All lines beginning with a semicolon *;* or a number sign *#* are considered + to be comments. Comment lines are ignored when parsing INI files. + + ### Example File Format + + A typical INI file might look like this: + + [section1] + ; some comment on section1 + var1 = foo + var2 = doodle + var3 = multiline values \ + are also possible + + [section2] + # another comment + var1 = baz + var2 = shoodle email: [email protected] executables: [] extensions: [] extra_rdoc_files: - History.txt files: -- .gitignore -- .travis.yml +- ".gitignore" +- ".travis.yml" - History.txt - README.md - Rakefile +- inifile.gemspec - lib/inifile.rb - test/data/bad_1.ini +- test/data/bad_2.ini - test/data/browscap.ini - test/data/comment.ini +- test/data/continuation.ini - test/data/escape.ini - test/data/global.ini - test/data/good.ini @@ -82,32 +106,32 @@ - test/data/mixed_comment.ini - test/data/multiline.ini - test/data/param.ini +- test/data/section.ini - test/test_inifile.rb homepage: http://rubygems.org/gems/inifile licenses: [] +metadata: {} post_install_message: rdoc_options: -- --main +- "--main" - README.md require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement - none: false requirements: - - - ! '>=' + - - ">=" - !ruby/object:Gem::Version version: '0' required_rubygems_version: !ruby/object:Gem::Requirement - none: false requirements: - - - ! '>=' + - - ">=" - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: inifile -rubygems_version: 1.8.24 +rubygems_version: 2.2.2 signing_key: -specification_version: 3 +specification_version: 4 summary: INI file reader and writer test_files: - test/test_inifile.rb diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/test/data/bad_2.ini new/test/data/bad_2.ini --- old/test/data/bad_2.ini 1970-01-01 01:00:00.000000000 +0100 +++ new/test/data/bad_2.ini 2014-08-01 18:30:01.000000000 +0200 @@ -0,0 +1,11 @@ +[section_one] +one = 1 +two = 2 + +[section_two] +close-quote = "some text without +a closing quote - should +be interesting + +[section_three] +wat = where is that closing quote? diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/test/data/comment.ini new/test/data/comment.ini --- old/test/data/comment.ini 1970-01-01 01:00:00.000000000 +0100 +++ new/test/data/comment.ini 2014-08-01 18:30:01.000000000 +0200 @@ -6,5 +6,5 @@ [section_two] # you can comment here one = 42 # and even here! multi = 20 \ # and here, too -+ 22 \= 42 ++ 22 = 42 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/test/data/continuation.ini new/test/data/continuation.ini --- old/test/data/continuation.ini 1970-01-01 01:00:00.000000000 +0100 +++ new/test/data/continuation.ini 2014-08-01 18:30:01.000000000 +0200 @@ -0,0 +1,6 @@ +[section_one] +one = 1 +two = 2 + +[section_two] +end-of-file = here is the last value \ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/test/data/good.ini new/test/data/good.ini --- old/test/data/good.ini 1970-01-01 01:00:00.000000000 +0100 +++ new/test/data/good.ini 2014-08-01 18:30:01.000000000 +0200 @@ -3,15 +3,15 @@ two = 2 [section_two] -three = 3 +three = -3 multi = multiline \ support ; comments should be ignored [section three] -four =4 -five=5 -six =6 +four =true +five=false # comments can go here +six =6.0 ; and here, too [section_four] [section_five] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/test/data/section.ini new/test/data/section.ini --- old/test/data/section.ini 1970-01-01 01:00:00.000000000 +0100 +++ new/test/data/section.ini 2014-08-01 18:30:01.000000000 +0200 @@ -0,0 +1,4 @@ +; section headers can be values +[section_one] +one=[value] +two=2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/test/test_inifile.rb new/test/test_inifile.rb --- old/test/test_inifile.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/test/test_inifile.rb 2014-08-01 18:30:01.000000000 +0200 @@ -11,13 +11,13 @@ def setup @ini_file = IniFile.new(:filename => 'test/data/good.ini') @contents = [ - ['section_one', 'one', '1'], - ['section_one', 'two', '2'], - ['section_two', 'three', '3'], + ['section_one', 'one', 1], + ['section_one', 'two', 2], + ['section_two', 'three', -3], ['section_two', 'multi', "multiline support"], - ['section three', 'four', '4'], - ['section three', 'five', '5'], - ['section three', 'six', '6'], + ['section three', 'four', true], + ['section three', 'five', false], + ['section three', 'six', 6.0], ['section_five', 'seven and eight', '7 & 8'] ].sort @@ -84,7 +84,7 @@ def test_delete_section assert_nil @ini_file.delete_section('section_nil') - h = {'one' => '1', 'two' => '2'} + h = {'one' => 1, 'two' => 2} assert_equal true, @ini_file.has_section?('section_one') assert_equal h, @ini_file.delete_section('section_one') assert_equal false, @ini_file.has_section?('section_one') @@ -148,7 +148,7 @@ assert @ini_file.eql?(@ini_file) assert @ini_file.eql?(@ini_file.clone) assert !@ini_file.eql?('string') - assert !@ini_file.eql?(IniFile.new('')) + assert !@ini_file.eql?(IniFile.new(:content => '')) end def test_freeze @@ -179,18 +179,18 @@ def test_index expected = { - 'one' => '1', - 'two' => '2' + 'one' => 1, + 'two' => 2 } assert_equal expected, @ini_file[:section_one] - expected = {'three' => '3', 'multi' => "multiline support"} + expected = {'three' => -3, 'multi' => "multiline support"} assert_equal expected, @ini_file['section_two'] expected = { - 'four' => '4', - 'five' => '5', - 'six' => '6', + 'four' => true, + 'five' => false, + 'six' => 6.0, } assert_equal expected, @ini_file['section three'] @@ -214,13 +214,11 @@ def test_match expected = { - "section_two" => - { - "three"=>"3", "multi"=>"multiline support" + "section_two" => { + "three" => -3, "multi" => "multiline support" }, - "section three" => - { - "four"=>"4", "five"=>"5", "six"=>"6" + "section three" => { + "four" => true, "five"=> false, "six" => 6.0 } } assert_equal expected, @ini_file.match(/(two|three)/) @@ -245,8 +243,8 @@ ini_file = IniFile.new(:filename => 'test/data/param.ini', :parameter => ':') assert ini_file.has_section?('section_one') - assert_equal '1', ini_file['section_one']['one'] - assert_equal '2', ini_file['section_one']['two'] + assert_equal 1, ini_file['section_one']['one'] + assert_equal 2, ini_file['section_one']['two'] # make sure we error out on files with bad lines assert_raise(IniFile::Error) {IniFile.new :filename => 'test/data/bad_1.ini'} @@ -254,7 +252,7 @@ def test_initialize_from_string_without_ending_newline content = "[section_one]\n foo=bar" - ini_file = IniFile.new(content) + ini_file = IniFile.new(:content => content) assert ini_file.has_section?('section_one') assert_equal 'bar', ini_file['section_one']['foo'] end @@ -262,7 +260,7 @@ def test_initialize_from_string content = File.read('test/data/good.ini') - ini_file = IniFile.new(content, :comment => ';') + ini_file = IniFile.new(:content => content, :comment => ';') assert ini_file.has_section?('section_one') assert ini_file.has_section?('section_two') assert ini_file.has_section?('section three') @@ -272,6 +270,35 @@ assert_equal '7 & 8', ini_file['section_five']['seven and eight'] end + def test_initialize_from_hash + hash = { + 'section one' => { + 'foo' => 'bar', + 'baz' => 'buz' + }, + 'colors' => { + 'perrywinkle' => '7e6ff3', + 'steelblue' => '4682b4' + }, + 'empty' => nil + } + + ini_file = IniFile.new(:content => hash) + assert ini_file.has_section?('section one') + assert ini_file.has_section?('colors') + assert ini_file.has_section?('empty') + + assert_equal %w[baz foo], ini_file['section one'].keys.sort + assert_equal 'bar', ini_file['section one']['foo'] + assert_equal 'buz', ini_file['section one']['baz'] + + assert_equal %w[perrywinkle steelblue], ini_file['colors'].keys.sort + assert_equal '7e6ff3', ini_file['colors']['perrywinkle'] + assert_equal '4682b4', ini_file['colors']['steelblue'] + + assert_empty ini_file['empty'] + end + def test_sections expected = [ 'section_one', 'section_two', 'section three', @@ -323,8 +350,8 @@ assert_equal 42, @ini_file['section_one']['two'] @ini_file.read - assert_equal '1', @ini_file['section_one']['one'] - assert_equal '2', @ini_file['section_one']['two'] + assert_equal 1, @ini_file['section_one']['one'] + assert_equal 2, @ini_file['section_one']['two'] @ini_file.read(:filename => 'test/data/mixed_comment.ini') assert_equal false, @ini_file.has_section?('section_two') @@ -392,61 +419,73 @@ assert_equal expected, multiple multiple = ini_file['empty_lines'] - expected = {'empty' => '', 'not_empty' => 'full'} + expected = {'empty' => nil, 'not_empty' => 'full'} assert_equal expected, multiple end def test_merge ini_file = @ini_file.merge(IniFile.load("test/data/merge.ini")) - assert_equal '3', ini_file['section_one']['one'] - assert_equal '2', ini_file['section_one']['two'] + assert_equal 3, ini_file['section_one']['one'] + assert_equal 2, ini_file['section_one']['two'] # make sure that the rest haven't changed - assert_equal '3', ini_file['section_two']['three'] + assert_equal(-3, ini_file['section_two']['three']) # and that we got any additional sections too - assert_equal '5', ini_file['section_five']['five'] + assert_equal 5, ini_file['section_five']['five'] # original object is unchanged - assert_equal '1', @ini_file['section_one']['one'] + assert_equal 1, @ini_file['section_one']['one'] end def test_merge_hash ini_file = @ini_file.merge({ - 'section_one' => { 'one' => '3' }, - 'section_five' => { 'five' => '5' } + 'section_one' => { 'one' => 3 }, + 'section_five' => { 'five' => 5 } }) - assert_equal '3', ini_file['section_one']['one'] - assert_equal '2', ini_file['section_one']['two'] + assert_equal 3, ini_file['section_one']['one'] + assert_equal 2, ini_file['section_one']['two'] # make sure that the rest haven't changed - assert_equal '3', ini_file['section_two']['three'] + assert_equal(-3, ini_file['section_two']['three']) # and that we got any additional sections too - assert_equal '5', ini_file['section_five']['five'] + assert_equal 5, ini_file['section_five']['five'] # original object is unchanged - assert_equal '1', @ini_file['section_one']['one'] + assert_equal 1, @ini_file['section_one']['one'] end - if RUBY_VERSION >= '1.9' - def test_parse_encoding - ini_file = IniFile.new(:filename => "test/data/browscap.ini", :encoding => 'ISO-8859-1') - assert_equal ini_file['www.substancia.com AutoHTTPAgent (ver *)']['Browser'], "Subst\xE2ncia".force_encoding('ISO-8859-1') - end + def test_merge_invalid_hash + bad_hash = { 'section_one' => [1, 2, 3, 4] } + assert_raise(IniFile::Error) { @ini_file.merge(bad_hash) } + + bad_hash = { 'foo' => 'bar' } + assert_raise(IniFile::Error) { @ini_file.merge(bad_hash) } - def test_write_encoding - tmp = 'test/data/tmp.ini' - File.delete tmp if Kernel.test(?f, tmp) + not_a_hash = [['section_one', ['foo','bar'], ['baz', 'buz']]] + assert_raise(IniFile::Error) { @ini_file.merge(not_a_hash) } - @ini_file = IniFile.new(:filename => tmp, :encoding => 'UTF-8') - @ini_file['testutf-8'] = {"utf-8" => "appr\u20accier"} + ini_file = @ini_file.merge nil + assert ini_file.eql?(@ini_file) + end - @ini_file.save(:filename => tmp) + def test_parse_encoding + ini_file = IniFile.new(:filename => "test/data/browscap.ini", :encoding => 'ISO-8859-1') + assert_equal ini_file['www.substancia.com AutoHTTPAgent (ver *)']['Browser'], "Subst\xE2ncia".force_encoding('ISO-8859-1') + end - test = File.open(tmp) - assert_equal test.external_encoding.to_s, 'UTF-8' - end + def test_write_encoding + tmp = 'test/data/tmp.ini' + File.delete tmp if Kernel.test(?f, tmp) + + @ini_file = IniFile.new(:filename => tmp, :encoding => 'UTF-8') + @ini_file['testutf-8'] = {"utf-8" => "appr\u20accier"} + + @ini_file.save(:filename => tmp) + + test = File.open(tmp) + assert_equal test.external_encoding.to_s, 'UTF-8' end def test_value_escaping @@ -461,32 +500,62 @@ assert_equal %Q{Escaping works\tinside quoted strings!}, escaped['quoted'] end + # disabling escaping is no longer supported def test_value_escaping_disabled ini_file = IniFile.load('test/data/escape.ini', :escape => false) escaped = ini_file['escaped'] - assert_equal %q{There is a tab\tcharacter in here somewhere}, escaped['tabs'] - assert_equal %q{Who uses these anyways?\r}, escaped['carriage return'] - assert_equal %q{Trust newline!\nAlways there when you need him.\nSplittin' those lines.}, escaped['newline'] - assert_equal %Q{Who'd be silly enough to put\\0 a null character in the middle of a string? Stroustrup would not approve!}, escaped['null'] - assert_equal %q{This string \\\\t contains \\\\n no \\\\r special \\\\0 characters!}, escaped['backslash'] - assert_equal %q{Escaping works\tinside quoted strings!}, escaped['quoted'] + assert_equal %Q{There is a tab\tcharacter in here somewhere}, escaped['tabs'] + assert_equal %Q{Who uses these anyways?\r}, escaped['carriage return'] + assert_equal %Q{Trust newline!\nAlways there when you need him.\nSplittin' those lines.}, escaped['newline'] + assert_equal %Q{Who'd be silly enough to put\0 a null character in the middle of a string? Stroustrup would not approve!}, escaped['null'] + assert_equal %q{This string \t contains \n no \r special \0 characters!}, escaped['backslash'] + assert_equal %Q{Escaping works\tinside quoted strings!}, escaped['quoted'] end def test_global_section ini_file = IniFile.load('test/data/global.ini') assert_equal %w[global], ini_file.sections - assert_equal '1', ini_file['global']['one'] - assert_equal '2', ini_file['global']['two'] + assert_equal 1, ini_file['global']['one'] + assert_equal 2, ini_file['global']['two'] end def test_default_global_section ini_file = IniFile.load('test/data/global.ini', :default => 'nonce') assert_equal %w[nonce], ini_file.sections - assert_equal '1', ini_file['nonce']['one'] - assert_equal '2', ini_file['nonce']['two'] + assert_equal 1, ini_file['nonce']['one'] + assert_equal 2, ini_file['nonce']['two'] + end + + def test_unescaped_section_header_as_value + ini_file = IniFile.load('test/data/section.ini') + + assert_equal %w[section_one], ini_file.sections + assert_equal '[value]', ini_file['section_one']['one'] + assert_equal 2, ini_file['section_one']['two'] + end + + def test_unmatched_quotes + # missing a closing quote should raise an error + assert_raise(IniFile::Error) { IniFile.load 'test/data/bad_2.ini' } + end + + def test_continuation_at_end_of_file + ini_file = IniFile.load('test/data/continuation.ini') + + assert_equal 1, ini_file['section_one']['one'] + assert_equal 2, ini_file['section_one']['two'] + + assert_equal 'here is the last value', ini_file['section_two']['end-of-file'] + end + + def test_empty_comment_string + ini_file = IniFile.load('test/data/merge.ini', :comment => nil) + + assert_equal 3, ini_file['section_one']['one'] + assert_equal 5, ini_file['section_five']['five'] end end -- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
