toml-rb-1.0.0/0000755000175000017500000000000013124502322012102 5ustar pravipravitoml-rb-1.0.0/README.md0000644000175000017500000000545613124502322013373 0ustar pravipravitoml-rb ======= [![Gem Version](https://badge.fury.io/rb/toml-rb.svg)](http://badge.fury.io/rb/toml-rb) [![Build Status](https://travis-ci.org/emancu/toml-rb.svg)](https://travis-ci.org/emancu/toml-rb) [![Code Climate](https://codeclimate.com/github/emancu/toml-rb/badges/gpa.svg)](https://codeclimate.com/github/emancu/toml-rb) [![Dependency Status](https://gemnasium.com/emancu/toml-rb.svg)](https://gemnasium.com/emancu/toml-rb) A [TomlRB](https://github.com/toml-lang/toml) parser using [Citrus](http://mjackson.github.io/citrus) library. TomlRB specs supported: `0.4.0` Installation ------------ $ gem install toml-rb Parser Usage ------------ ```ruby require 'toml' # From a file! path = File.join(File.dirname(__FILE__), 'path', 'to', 'file') TomlRB.load_file(path) # From a stream! stream = <<-EOS title = "wow!" [awesome] you = true others = false EOS TomlRB.parse(stream) # => {"title"=>"wow!", "awesome"=>{"you"=>true, "others"=>false}} # You want symbols as your keys? No problem! TomlRB.load_file(path, symbolize_keys: true) # Works the same for TomlRB.parse ``` Dumper Usage ------------ ```ruby require 'toml' # Simple example TomlRB.dump( simple: true) # => "simple = true\n" # Complex example hash = { "title"=>"wow!", "awesome"=> { "you"=>true, "others"=>false } } TomlRB.dump(hash) # => "title = \"wow!\"\n[awesome]\nothers = false\nyou = true\n" ``` Contributing ------------ 1. Fork it 2. Bundle it `$ dep install` (install [dep](https://github.com/cyx/dep) if you don't have it) 3. Create your feature branch `git checkout -b my-new-feature` 4. Add tests and commit your changes `git commit -am 'Add some feature'` 5. Run tests `$ rake` 6. Push the branch `git push origin my-new-feature` 7. Create new Pull Request License ------- MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. toml-rb-1.0.0/lib/0000755000175000017500000000000013124502322012650 5ustar pravipravitoml-rb-1.0.0/lib/toml-rb.rb0000644000175000017500000000427313124502322014557 0ustar pravipravirequire_relative '../init' module TomlRB # Public: Returns a hash from *TomlRB* content. # # content - TomlRB string to be parsed. # options - The Hash options used to refine the parser (default: {}): # :symbolize_keys - true|false (optional). # # # Examples # # TomlRB.parse('[group]') # # => {"group"=>{}} # # TomlRB.parse('title = "TomlRB parser"') # # => {"title"=>"TomlRB parser"} # # TomlRB.parse('[group]', symbolize_keys: true) # # => {group: {}} # # TomlRB.parse('title = "TomlRB parser"', symbolize_keys: true) # # => {title: "TomlRB parser"} # # # Returns a Ruby hash representation of the content according to TomlRB spec. # Raises ValueOverwriteError if a key is overwritten. # Raises ParseError if the content has invalid TomlRB. def self.parse(content, options = {}) Parser.new(content, options).hash end # Public: Returns a hash from a *TomlRB* file. # # path - TomlRB File path # options - The Hash options used to refine the parser (default: {}): # :symbolize_keys - true|false (optional). # # # Examples # # TomlRB.load_file('/tmp/simple.toml') # # => {"group"=>{}} # # TomlRB.load_file('/tmp/simple.toml', symbolize_keys: true) # # => {group: {}} # # # Returns a Ruby hash representation of the content. # Raises ValueOverwriteError if a key is overwritten. # Raises ParseError if the content has invalid TomlRB. # Raises Errno::ENOENT if the file cannot be found. # Raises Errno::EACCES if the file cannot be accessed. def self.load_file(path, options = {}) TomlRB.parse(File.read(path), options) end # Public: Returns a *TomlRB* string from a Ruby Hash. # # hash - Ruby Hash to be dumped into *TomlRB* # # # Examples # # TomlRB.dump(title: 'TomlRB dump') # # => "simple = true\n" # # hash = { # "title"=>"wow!", # "awesome"=> { # "you"=>true, # "others"=>false # } # } # # TomlRB.dump(hash) # # => "title = \"wow!\"\n[awesome]\nothers = false\nyou = true\n" # # # Returns a TomlRB string representing the hash. def self.dump(hash) Dumper.new(hash).toml_str end end toml-rb-1.0.0/lib/toml-rb/0000755000175000017500000000000013124502322014224 5ustar pravipravitoml-rb-1.0.0/lib/toml-rb/parser.rb0000644000175000017500000000173313124502322016051 0ustar pravipravimodule TomlRB class Parser attr_reader :hash def initialize(content, options = {}) @hash = {} @visited_keys = [] @current = @hash @symbolize_keys = options[:symbolize_keys] begin parsed = TomlRB::Document.parse(content) parsed.matches.map(&:value).compact.each { |m| m.accept_visitor(self) } rescue Citrus::ParseError => e raise TomlRB::ParseError.new(e.message) end end # Read about the Visitor pattern # http://en.wikipedia.org/wiki/Visitor_pattern def visit_table_array(table_array) table_array_key = table_array.full_key @visited_keys.reject! { |k| k.start_with? table_array_key } @current = table_array.navigate_keys @hash, @symbolize_keys end def visit_keygroup(keygroup) @current = keygroup.navigate_keys @hash, @visited_keys, @symbolize_keys end def visit_keyvalue(keyvalue) keyvalue.assign @current, @symbolize_keys end end end toml-rb-1.0.0/lib/toml-rb/string.rb0000644000175000017500000000271313124502322016062 0ustar pravipravimodule TomlRB # Used in primitive.citrus module BasicString SPECIAL_CHARS = { '\\0' => "\0", '\\t' => "\t", '\\b' => "\b", '\\f' => "\f", '\\n' => "\n", '\\r' => "\r", '\\"' => '"', '\\\\' => '\\' }.freeze def value aux = TomlRB::BasicString.transform_escaped_chars first.value aux[1...-1] end # Replace the unicode escaped characters with the corresponding character # e.g. \u03B4 => ? def self.decode_unicode(str) [str[2..-1].to_i(16)].pack('U') end def self.transform_escaped_chars(str) str.gsub(/\\(u[\da-fA-F]{4}|U[\da-fA-F]{8}|.)/) do |m| if m.size == 2 SPECIAL_CHARS[m] || parse_error(m) else decode_unicode(m).force_encoding('UTF-8') end end end def self.parse_error(m) fail ParseError.new "Escape sequence #{m} is reserved" end end module LiteralString def value first.value[1...-1] end end module MultilineString def value return '' if captures[:text].empty? aux = captures[:text].first.value # Remove spaces on multilined Singleline strings aux.gsub!(/\\\r?\n[\n\t\r ]*/, '') TomlRB::BasicString.transform_escaped_chars aux end end module MultilineLiteral def value return '' if captures[:text].empty? aux = captures[:text].first.value aux.gsub(/\\\r?\n[\n\t\r ]*/, '') end end end toml-rb-1.0.0/lib/toml-rb/table_array.rb0000644000175000017500000000212413124502322017035 0ustar pravipravimodule TomlRB class TableArray def initialize(nested_keys) @nested_keys = nested_keys end def navigate_keys(hash, symbolize_keys = false) last_key = @nested_keys.pop # Go over the parent keys @nested_keys.each do |key| key = symbolize_keys ? key.to_sym : key hash[key] = {} unless hash[key] if hash[key].is_a? Array hash[key] << {} if hash[key].empty? hash = hash[key].last else hash = hash[key] end end # Define Table Array if hash[last_key].is_a? Hash fail TomlRB::ParseError, "#{last_key} was defined as hash but is now redefined as a table!" end hash[last_key] = [] unless hash[last_key] hash[last_key] << {} hash[last_key].last end def accept_visitor(parser) parser.visit_table_array self end def full_key @nested_keys.join('.') end end # Used in document.citrus module TableArrayParser def value TomlRB::TableArray.new(captures[:stripped_key].map(&:value)) end end end toml-rb-1.0.0/lib/toml-rb/array.rb0000644000175000017500000000042113124502322015664 0ustar pravipravimodule TomlRB module ArrayParser def value elements = captures[:elements].first return [] unless elements if elements.captures.key? :string elements.captures[:string].map(&:value) else eval(to_str) end end end end toml-rb-1.0.0/lib/toml-rb/keygroup.rb0000644000175000017500000000213113124502322016413 0ustar pravipravimodule TomlRB class Keygroup def initialize(nested_keys) @nested_keys = nested_keys end def navigate_keys(hash, visited_keys, symbolize_keys = false) ensure_key_not_defined(visited_keys) @nested_keys.each do |key| key = symbolize_keys ? key.to_sym : key hash[key] = {} unless hash.key?(key) element = hash[key] hash = element.is_a?(Array) ? element.last : element # check that key has not been defined before as a scalar value fail ValueOverwriteError.new(key) unless hash.is_a?(Hash) end hash end # Fail if the key was already defined with a ValueOverwriteError def ensure_key_not_defined(visited_keys) fail ValueOverwriteError.new(full_key) if visited_keys.include?(full_key) visited_keys << full_key end def accept_visitor(parser) parser.visit_keygroup self end def full_key @nested_keys.join('.') end end # Used in document.citrus module KeygroupParser def value TomlRB::Keygroup.new(captures[:stripped_key].map(&:value)) end end end toml-rb-1.0.0/lib/toml-rb/grammars/0000755000175000017500000000000013124502322016035 5ustar pravipravitoml-rb-1.0.0/lib/toml-rb/grammars/array.citrus0000644000175000017500000000146313124502322020412 0ustar pravipravigrammar TomlRB::Arrays include TomlRB::Primitive rule array ("[" array_comments (elements)? space ","? array_comments "]" indent?) end rule array_comments (indent? comment* indent?) end rule elements float_array | string_array | array_array | integer_array | datetime_array | bool_array end rule float_array ((exponent | float) (space "," array_comments (exponent | float))*) end rule string_array (string (space "," array_comments string)*) end rule array_array (array (space "," array_comments array)*) end rule integer_array (integer (space "," array_comments integer)*) end rule datetime_array (datetime (space "," array_comments datetime)*) end rule bool_array (bool (space "," array_comments bool)*) end end toml-rb-1.0.0/lib/toml-rb/grammars/primitive.citrus0000644000175000017500000000400713124502322021301 0ustar pravipravigrammar TomlRB::Primitive include TomlRB::Helper rule primitive string | bool | datetime | number end ## # String rules ## rule string multiline_string | multiline_literal | basic_string | literal_string end rule basic_string (/(["])(?:\\?.)*?\1/ space) end rule literal_string (/(['])(?:\\?.)*?\1/ space) end rule multiline_string ('"""' line_break* (text:~('"""' !'"')|'') '"""' space) end rule multiline_literal ("'''" line_break* (text:~("'''" !"'")|'') "'''" space) end ## # Date time rules ## rule datetime ( skeleton:(datetime_skeleton | date_skeleton) ("Z" | date_offset | [,\.] frac:(/\d/1*6) date_offset?)? ) { skeleton = captures[:skeleton].first y,m,d,h,mi,s = skeleton.value offset = captures[:date_offset].first || "+00:00" sec_frac = captures[:frac].first || 0 s = "#{s}.#{sec_frac}".to_f Time.new(*[y,m,d,h,mi,s,offset.to_s]) } end rule datetime_skeleton (y:/\d\d\d\d/ "-" m:/\d\d/ "-" d:/\d\d/ "T" h:/\d\d/ ":" mi:/\d\d/ ":" s:/\d\d/) { [:y,:m,:d,:h,:mi,:s].map{ |s| capture(s).value } } end rule date_offset offset:(sign /\d\d/ ":" /\d\d/) end rule date_skeleton (y:/\d\d\d\d/ "-" m:/\d\d/ "-" d:/\d\d/) { [:y,:m,:d].map{ |s| capture(s).value } + [0, 0, 0] } end ## # Number rules ## rule number exponent | float | integer end rule float (integer '.' integer) { to_str.to_f } end rule exponent ( (float | integer) [eE] integer) { to_str.to_f } end rule integer (sign? [0-9_]+) { to_str.to_i } end rule sign '+' | '-' end ## # Boolean rules ## rule bool true | false end rule true 'true' { true } end rule false 'false' { false } end ## # Key rules ## rule key quoted_key | bare_key end rule bare_key [a-zA-Z0-9_-]+ end rule quoted_key string end end toml-rb-1.0.0/lib/toml-rb/grammars/helper.citrus0000644000175000017500000000035213124502322020547 0ustar pravipravigrammar TomlRB::Helper rule comment (space? "#" (~line_break)* line_break?) { nil } end rule space [ \t]* end rule indent [ \t\r\n]* end rule line_break (space? "\n" | space? "\r\n") { nil } end end toml-rb-1.0.0/lib/toml-rb/grammars/document.citrus0000644000175000017500000000201613124502322021105 0ustar pravipravigrammar TomlRB::Document include TomlRB::Primitive include TomlRB::Arrays rule document (comment | table_array | keygroup | keyvalue | line_break)* end rule table_array (space? '[[' stripped_key ("." stripped_key)* ']]' comment?) end rule keygroup (space? '[' stripped_key ("." stripped_key)* ']' comment?) end rule keyvalue (stripped_key '=' space? v:(toml_values) comment?) end rule inline_table (space? '{' (keyvalue (',' keyvalue)*)? space? '}' ) end rule inline_table_array ("[" array_comments inline_table_array_elements space ","? array_comments "]" indent?) end rule inline_table_array_elements (inline_table (space "," array_comments inline_table)*) end rule toml_values primitive | inline_table | array | inline_table_array end rule stripped_key (space? key space?) { captures[:key].first.value } end end toml-rb-1.0.0/lib/toml-rb/dumper.rb0000644000175000017500000000460513124502322016052 0ustar pravipravimodule TomlRB class Dumper attr_reader :toml_str def initialize(hash) @toml_str = '' visit(hash, []) end private def visit(hash, prefix, extra_brackets = false) simple_pairs, nested_pairs, table_array_pairs = sort_pairs hash if prefix.any? && (simple_pairs.any? || hash.empty?) print_prefix prefix, extra_brackets end dump_pairs simple_pairs, nested_pairs, table_array_pairs, prefix end def sort_pairs(hash) nested_pairs = [] simple_pairs = [] table_array_pairs = [] hash.keys.sort.each do |key| val = hash[key] element = [key, val] if val.is_a? Hash nested_pairs << element elsif val.is_a?(Array) && val.first.is_a?(Hash) table_array_pairs << element else simple_pairs << element end end [simple_pairs, nested_pairs, table_array_pairs] end def dump_pairs(simple, nested, table_array, prefix = []) # First add simple pairs, under the prefix dump_simple_pairs simple dump_nested_pairs nested, prefix dump_table_array_pairs table_array, prefix end def dump_simple_pairs(simple_pairs) simple_pairs.each do |key, val| key = quote_key(key) unless bare_key? key @toml_str << "#{key} = #{to_toml(val)}\n" end end def dump_nested_pairs(nested_pairs, prefix) nested_pairs.each do |key, val| key = quote_key(key) unless bare_key? key visit val, prefix + [key], false end end def dump_table_array_pairs(table_array_pairs, prefix) table_array_pairs.each do |key, val| key = quote_key(key) unless bare_key? key aux_prefix = prefix + [key] val.each do |child| print_prefix aux_prefix, true args = sort_pairs(child) << aux_prefix dump_pairs(*args) end end end def print_prefix(prefix, extra_brackets = false) new_prefix = prefix.join('.') new_prefix = '[' + new_prefix + ']' if extra_brackets @toml_str += "[" + new_prefix + "]\n" end def to_toml(obj) if obj.is_a? Time obj.strftime('%Y-%m-%dT%H:%M:%SZ') else obj.inspect end end def bare_key?(key) !!key.to_s.match(/^[a-zA-Z0-9_-]*$/) end def quote_key(key) '"' + key.gsub('"', '\\"') + '"' end end end toml-rb-1.0.0/lib/toml-rb/keyvalue.rb0000644000175000017500000000177713124502322016412 0ustar pravipravimodule TomlRB class Keyvalue attr_reader :value, :symbolize_keys def initialize(key, value) @key = key @value = value @symbolize_keys = false end def assign(hash, symbolize_keys = false) @symbolize_keys = symbolize_keys fail ValueOverwriteError.new(key) if hash.key?(key) hash[key] = visit_value @value end def visit_inline_table(inline_table) result = {} inline_table.value(@symbolize_keys).each do |k, v| result[key k] = visit_value v end result end def key(a_key = @key) symbolize_keys ? a_key.to_sym : a_key end def accept_visitor(parser) parser.visit_keyvalue self end private def visit_value(a_value) return a_value unless a_value.respond_to? :accept_visitor a_value.accept_visitor self end end # Used in document.citrus module KeyvalueParser def value TomlRB::Keyvalue.new(capture(:stripped_key).value, capture(:v).value) end end end toml-rb-1.0.0/lib/toml-rb/inline_table.rb0000644000175000017500000000275513124502322017207 0ustar pravipravimodule TomlRB class InlineTable attr_reader :symbolize_keys def initialize(keyvalue_pairs) @pairs = keyvalue_pairs @symbolize_keys = false end def value(symbolize_keys = false) if (@symbolize_keys = symbolize_keys) tuple = ->(kv) { [kv.key.to_sym, visit_value(kv.value)] } else tuple = ->(kv) { [kv.key, visit_value(kv.value)] } end Hash[@pairs.map(&tuple)] end def visit_inline_table(inline_table) result = {} inline_table.value(@symbolize_keys).each do |k, v| result[key k] = visit_value v end result end def accept_visitor(keyvalue) keyvalue.visit_inline_table self end private def visit_value(a_value) return a_value unless a_value.respond_to? :accept_visitor a_value.accept_visitor self end def key(a_key) symbolize_keys ? a_key.to_sym : a_key end end class InlineTableArray def initialize(inline_tables) @inline_tables = inline_tables end def value(symbolize_keys = false) @inline_tables.map { |it| it.value(symbolize_keys) } end end module InlineTableParser def value TomlRB::InlineTable.new captures[:keyvalue].map(&:value) end end module InlineTableArrayParser def value tables = captures[:inline_table_array_elements].map do |x| x.captures[:inline_table] end TomlRB::InlineTableArray.new(tables.flatten.map(&:value)).value end end end toml-rb-1.0.0/lib/toml-rb/errors.rb0000644000175000017500000000053613124502322016071 0ustar pravipravimodule TomlRB # Parent class for all TomlRB errors Error = Class.new(StandardError) # Error related to parsing. ParseError = Class.new(Error) # Overwrite error class ValueOverwriteError < Error attr_accessor :key def initialize(key) @key = key super "Key #{key.inspect} is defined more than once" end end end toml-rb-1.0.0/init.rb0000644000175000017500000000120713124502322013372 0ustar pravipravirequire 'citrus' ROOT = File.dirname(File.expand_path(__FILE__)) require "#{ROOT}/lib/toml-rb/errors" require "#{ROOT}/lib/toml-rb/array" require "#{ROOT}/lib/toml-rb/string" require "#{ROOT}/lib/toml-rb/table_array" require "#{ROOT}/lib/toml-rb/inline_table" require "#{ROOT}/lib/toml-rb/keyvalue" require "#{ROOT}/lib/toml-rb/keygroup" require "#{ROOT}/lib/toml-rb/parser" require "#{ROOT}/lib/toml-rb/dumper" Citrus.load "#{ROOT}/lib/toml-rb/grammars/helper.citrus" Citrus.load "#{ROOT}/lib/toml-rb/grammars/primitive.citrus" Citrus.load "#{ROOT}/lib/toml-rb/grammars/array.citrus" Citrus.load "#{ROOT}/lib/toml-rb/grammars/document.citrus" toml-rb-1.0.0/toml-rb.gemspec0000644000175000017500000000122313124502322015021 0ustar pravipraviGem::Specification.new do |s| s.name = 'toml-rb' s.version = '1.0.0' s.date = Time.now.strftime('%Y-%m-%d') s.summary = 'Toml parser in ruby, for ruby.' s.description = 'A Toml parser using Citrus parsing library. ' s.authors = ['Emiliano Mancuso', 'Lucas Tolchinsky'] s.email = ['emiliano.mancuso@gmail.com', 'lucas.tolchinsky@gmail.com'] s.homepage = 'http://github.com/emancu/toml-rb' s.license = 'MIT' s.files = Dir[ 'README.md', 'Rakefile', 'lib/**/*.rb', 'lib/**/*.citrus', '*.gemspec', 'test/*.*', 'init.rb' ] s.add_dependency 'citrus', '~> 3.0', '> 3.0' end toml-rb-1.0.0/Rakefile0000644000175000017500000000015213124502322013545 0ustar pravipravirequire 'rake/testtask' Rake::TestTask.new do |t| t.pattern = 'test/*_test.rb' end task default: :test toml-rb-1.0.0/test/0000755000175000017500000000000013124502322013061 5ustar pravipravitoml-rb-1.0.0/test/toml_test.rb0000644000175000017500000000607413124502322015427 0ustar pravipravirequire_relative 'helper' require_relative 'toml_examples' require 'json' class TomlTest < Minitest::Test def test_file_v_0_4_0 path = File.join(File.dirname(__FILE__), 'example-v0.4.0.toml') parsed = TomlRB.load_file(path) hash = TomlRB::Examples.example_v_0_4_0 assert_equal hash['Array'], parsed['Array'] assert_equal hash['Booleans'], parsed['Booleans'] assert_equal hash['Datetime'], parsed['Datetime'] assert_equal hash['Float'], parsed['Float'] assert_equal hash['Integer'], parsed['Integer'] assert_equal hash['String'], parsed['String'] assert_equal hash['Table'], parsed['Table'] assert_equal hash['products'], parsed['products'] assert_equal hash['fruit'], parsed['fruit'] end def test_file path = File.join(File.dirname(__FILE__), 'example.toml') parsed = TomlRB.load_file(path) assert_equal TomlRB::Examples.example, parsed end def test_hard_example path = File.join(File.dirname(__FILE__), 'hard_example.toml') parsed = TomlRB.load_file(path) assert_equal TomlRB::Examples.hard_example, parsed end def test_symbolize_keys path = File.join(File.dirname(__FILE__), 'example.toml') parsed = TomlRB.load_file(path, symbolize_keys: true) hash = { title: 'TomlRB Example', owner: { name: 'Tom Preston-Werner', organization: 'GitHub', bio: "GitHub Cofounder & CEO\nLikes tater tots and beer.", dob: Time.utc(1979, 05, 27, 07, 32, 00) }, database: { server: '192.168.1.1', ports: [8001, 8001, 8002], connection_max: 5000, enabled: true }, servers: { alpha: { ip: '10.0.0.1', dc: 'eqdc10' }, beta: { ip: '10.0.0.2', dc: 'eqdc10' } }, clients: { data: [%w(gamma delta), [1, 2]], hosts: %w(alpha omega) }, amqp: { exchange: { durable: true, auto_delete: false } } } assert_equal(hash, parsed) end def test_line_break parsed = TomlRB.parse("hello = 'world'\r\nline_break = true") assert_equal({ 'hello' => 'world', 'line_break' => true }, parsed) end def compare_toml_files(folder, file = nil, &block) file ||= '*' Dir["test/examples/#{folder}/#{file}.json"].each do |json_file| toml_file = File.join(File.dirname(json_file), File.basename(json_file, '.json')) + '.toml' begin toml = TomlRB.load_file(toml_file) rescue TomlRB::Error => e assert false, "Error: #{e} in #{toml_file}" end json = JSON.parse(File.read(json_file)) block.call(json, toml, toml_file) end end def test_valid_cases compare_toml_files 'valid' do |json, toml, file| assert_equal json, toml, "In file '#{file}'" end end def test_invalid_cases file = '*' Dir["test/examples/invalid/#{file}.toml"].each do |toml_file| assert_raises(TomlRB::Error, "For file #{toml_file}") do TomlRB.load_file(toml_file) end end end end toml-rb-1.0.0/test/example-v0.4.0.toml0000644000175000017500000001217413124502322016241 0ustar pravipravi################################################################################ ## Comment # Speak your mind with the hash symbol. They go from the symbol to the end of # the line. ################################################################################ ## Table # Tables (also known as hash tables or dictionaries) are collections of # key/value pairs. They appear in square brackets on a line by themselves. [table] key = "value" # Yeah, you can do this. # Nested tables are denoted by table names with dots in them. Name your tables # whatever crap you please, just don't use #, ., [ or ]. [table.subtable] key = "another value" # You don't need to specify all the super-tables if you don't want to. TomlRB # knows how to do it for you. # [x] you # [x.y] don't # [x.y.z] need these [x.y.z.w] # for this to work ################################################################################ ## Inline Table # Inline tables provide a more compact syntax for expressing tables. They are # especially useful for grouped data that can otherwise quickly become verbose. # Inline tables are enclosed in curly braces `{` and `}`. No newlines are # allowed between the curly braces unless they are valid within a value. [table.inline] name = { first = "Tom", last = "Preston-Werner" } point = { x = 1, y = 2 } ################################################################################ ## String # There are four ways to express strings: basic, multi-line basic, literal, and # multi-line literal. All strings must contain only valid UTF-8 characters. [string.basic] basic = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF." [string.multiline] # The following strings are byte-for-byte equivalent: key1 = "One\nTwo" key2 = """One\nTwo""" key3 = """ One Two""" [string.multiline.continued] # The following strings are byte-for-byte equivalent: key1 = "The quick brown fox jumps over the lazy dog." key2 = """ The quick brown \ fox jumps over \ the lazy dog.""" key3 = """\ The quick brown \ fox jumps over \ the lazy dog.\ """ [string.literal] # What you see is what you get. winpath = 'C:\Users\nodejs\templates' winpath2 = '\\ServerX\admin$\system32\' quoted = 'Tom "Dubs" Preston-Werner' regex = '<\i\c*\s*>' [string.literal.multiline] regex2 = '''I [dw]on't need \d{2} apples''' lines = ''' The first newline is trimmed in raw strings. All other whitespace is preserved. ''' ################################################################################ ## Integer # Integers are whole numbers. Positive numbers may be prefixed with a plus sign. # Negative numbers are prefixed with a minus sign. [integer] key1 = +99 key2 = 42 key3 = 0 key4 = -17 [integer.underscores] # For large numbers, you may use underscores to enhance readability. Each # underscore must be surrounded by at least one digit. key1 = 1_000 key2 = 5_349_221 key3 = 1_2_3_4_5 # valid but inadvisable ################################################################################ ## Float # A float consists of an integer part (which may be prefixed with a plus or # minus sign) followed by a fractional part and/or an exponent part. [float.fractional] key1 = +1.0 key2 = 3.1415 key3 = -0.01 [float.exponent] key1 = 5e+22 key2 = 1e6 key3 = -2E-2 [float.both] key = 6.626e-34 [float.underscores] key1 = 9_224_617.445_991_228_313 key2 = 1e1_000 ################################################################################ ## Boolean # Booleans are just the tokens you're used to. Always lowercase. [boolean] True = true False = false ################################################################################ ## Datetime # Datetimes are RFC 3339 dates. [datetime] key1 = 1979-05-27T07:32:00Z key2 = 1979-05-27T00:32:00-07:00 key3 = 1979-05-27T00:32:00.999999-07:00 ################################################################################ ## Array # Arrays are square brackets with other primitives inside. Whitespace is # ignored. Elements are separated by commas. Data types may not be mixed. [array] key1 = [ 1, 2, 3 ] key2 = [ "red", "yellow", "green" ] key3 = [ [ 1, 2 ], [3, 4, 5] ] key4 = [ [ 1, 2 ], ["a", "b", "c"] ] # this is ok # Arrays can also be multiline. So in addition to ignoring whitespace, arrays # also ignore newlines between the brackets. Terminating commas are ok before # the closing bracket. key5 = [ 1, 2, 3 ] key6 = [ 1, 2, # this is ok ] ################################################################################ ## Array of Tables # These can be expressed by using a table name in double brackets. Each table # with the same double bracketed name will be an element in the array. The # tables are inserted in the order encountered. [[products]] name = "Hammer" sku = 738594937 [[products]] [[products]] name = "Nail" sku = 284758393 color = "gray" # You can create nested arrays of tables as well. [[fruit]] name = "apple" [fruit.physical] color = "red" shape = "round" [[fruit.variety]] name = "red delicious" [[fruit.variety]] name = "granny smith" [[fruit]] name = "banana" [[fruit.variety]] name = "plantain" toml-rb-1.0.0/test/dumper_test.rb0000644000175000017500000000477613124502322015757 0ustar pravipravirequire_relative 'helper' class DumperTest < Minitest::Test def test_dump_empty dumped = TomlRB.dump({}) assert_equal('', dumped) end def test_dump_types dumped = TomlRB.dump(string: 'TomlRB "dump"') assert_equal("string = \"TomlRB \\\"dump\\\"\"\n", dumped) dumped = TomlRB.dump(float: -13.24) assert_equal("float = -13.24\n", dumped) dumped = TomlRB.dump(int: 1234) assert_equal("int = 1234\n", dumped) dumped = TomlRB.dump(true: true) assert_equal("true = true\n", dumped) dumped = TomlRB.dump(false: false) assert_equal("false = false\n", dumped) dumped = TomlRB.dump(array: [1, 2, 3]) assert_equal("array = [1, 2, 3]\n", dumped) dumped = TomlRB.dump(array: [[1, 2], %w(weird one)]) assert_equal("array = [[1, 2], [\"weird\", \"one\"]]\n", dumped) dumped = TomlRB.dump(datetime: Time.utc(1986, 8, 28, 15, 15)) assert_equal("datetime = 1986-08-28T15:15:00Z\n", dumped) end def test_dump_nested_attributes hash = { nested: { hash: { deep: true } } } dumped = TomlRB.dump(hash) assert_equal("[nested.hash]\ndeep = true\n", dumped) hash[:nested].merge!(other: 12) dumped = TomlRB.dump(hash) assert_equal("[nested]\nother = 12\n[nested.hash]\ndeep = true\n", dumped) hash[:nested].merge!(nest: { again: 'it never ends' }) dumped = TomlRB.dump(hash) toml = <<-EOS.gsub(/^ {6}/, '') [nested] other = 12 [nested.hash] deep = true [nested.nest] again = "it never ends" EOS assert_equal(toml, dumped) hash = { non: { 'bare."keys"' => { "works" => true } } } dumped = TomlRB.dump(hash) assert_equal("[non.\"bare.\\\"keys\\\"\"]\nworks = true\n", dumped) hash = { hola: [{ chau: 4 }, { chau: 3 }] } dumped = TomlRB.dump(hash) assert_equal("[[hola]]\nchau = 4\n[[hola]]\nchau = 3\n", dumped) end def test_print_empty_tables hash = { plugins: { cpu: { foo: "bar", baz: 1234 }, disk: {}, io: {} } } dumped = TomlRB.dump(hash) toml = <<-EOS.gsub(/^ {6}/, '') [plugins.cpu] baz = 1234 foo = "bar" [plugins.disk] [plugins.io] EOS assert_equal toml, dumped end def test_dump_array_tables hash = { fruit: [{ physical: { color: "red" } }, { physical: { color: "blue" } }] } dumped = TomlRB.dump(hash) toml = <<-EOS.gsub(/^ {6}/, '') [[fruit]] [fruit.physical] color = "red" [[fruit]] [fruit.physical] color = "blue" EOS assert_equal toml, dumped end end toml-rb-1.0.0/test/hard_example.toml0000644000175000017500000000300113124502322016401 0ustar pravipravi# Test file for TomlRB # Only this one tries to emulate a TomlRB file written by a user of the kind of parser writers probably hate # This part you'll really hate [the] test_string = "You'll hate me after this - #" # " Annoying, isn't it? [the.hard] test_array = [ "] ", " # "] # ] There you go, parse this! test_array2 = [ "Test #11 ]proved that", "Experiment #9 was a success" ] # You didn't think it'd as easy as chucking out the last #, did you? another_test_string = " Same thing, but with a string #" harder_test_string = " And when \"'s are in the string, along with # \"" # "and comments are there too" # Things will get harder [the.hard."bit#"] "what?" = "You don't think some user won't do that?" multi_line_array = [ "]", # ] Oh yes I did ] [parent.child1] key = 'value' [[parent.child2]] key2 = 'value' [[parent.child2]] key3 = 'value' [[a.b]] c = 3 # Each of the following keygroups/key value pairs should produce an error. Uncomment to them to test #[error] if you didn't catch this, your parser is broken #string = "Anything other than tabs, spaces and newline after a keygroup or key value pair has ended should produce an error unless it is a comment" like this #array = [ # "This might most likely happen in multiline arrays", # Like here, # "or here, # and here" # ] End of array comment, forgot the # #number = 3.14 pi <--again forgot the # toml-rb-1.0.0/test/example.toml0000644000175000017500000000130113124502322015404 0ustar pravipravi# This is a TomlRB document. Boom. title = "TomlRB Example" [owner] name = "Tom Preston-Werner" organization = "GitHub" bio = "GitHub Cofounder & CEO\nLikes tater tots and beer." dob = 1979-05-27T07:32:00Z # First class dates? Why not? [database] server = "192.168.1.1" ports = [ 8001, 8001, 8002 ] connection_max = 5000 enabled = true [servers] # You can indent as you please. Tabs or spaces. TomlRB don't care. [servers.alpha] ip = "10.0.0.1" dc = "eqdc10" [servers.beta] ip = "10.0.0.2" dc = "eqdc10" [clients] data = [ ["gamma", "delta"], [1, 2] ] # Line breaks are OK when inside arrays hosts = [ "alpha", "omega" ] [amqp] exchange = {durable = true, auto_delete = false} toml-rb-1.0.0/test/toml_examples.rb0000644000175000017500000001165713124502322016271 0ustar pravipraviclass TomlRB::Examples def self.example_v_0_4_0 { "table" => { "key" => "value", "subtable" => { "key" => "another value" }, "inline" => { "name" => { "first" => "Tom", "last" => "Preston-Werner" }, "point" => { "x" => 1, "y" => 2 } } }, "x" => { "y" => { "z" => { "w" => {} } } }, "string" => { "basic" => { "basic" => "I'm a string. \"You can quote me\". Name\tJos\\u00E9\nLocation\tSF." }, "multiline" => { "key1" => "One\nTwo", "key2" => "One\nTwo", "key3" => "One\nTwo", "continued" => { "key1" => "The quick brown fox jumps over the lazy dog.", "key2" => "The quick brown fox jumps over the lazy dog.", "key3" => "The quick brown fox jumps over the lazy dog." } }, "literal" => { "winpath" => "C:\\Users\\nodejs\\templates", "winpath2" => "\\\\ServerX\\admin$\\system32\\", "quoted" => "Tom\"Dubs\"Preston-Werner", "regex" => "<\\i\\c*\\s*>", "multiline" => { "regex2" => "I[ dw ]on'tneed\\d{ 2 }apples", "lines" => "Thefirstnewlineis\ntrimmedinrawstrings.\nAllotherwhitespace\nispreserved.\n" } } }, "integer" => { "key1" => 99, "key2" => 42, "key3" => 0, "key4" => -17, "underscores" => { "key1" => 1000, "key2" => 5349221, "key3" => 12345 } }, "float" => { "fractional" => { "key1" => 1.0, "key2" => 3.1415, "key3" => -0.01 }, "exponent" => { "key1" => 5.0e+22, "key2" => 1000000.0, "key3" => -0.02 }, "both" => { "key" => 6.626e-34 }, "underscores" => { "key1" => 9224617.445991227, "key2" => 1e1_000 } }, "boolean" => { "True" => true, "False" => false }, "datetime" => { "key1" => Time.utc(1979, 05, 27, 07, 32, 0), "key2" => Time.new(1979, 05, 27, 00, 32, 0, '-07:00'), "key3" => Time.new(1979, 05, 27, 00, 32, 0.999999, '-07:00') }, "array" => { "key1" => [1, 2, 3], "key2" => %w(red yellow green), "key3" => [[1, 2], [3, 4, 5]], "key4" => [[1, 2], %w(a b c)], "key5" => [1, 2, 3], "key6" => [1, 2] }, "products" => [ { "name" => "Hammer", "sku" => 738594937 }, {}, { "name" => "Nail", "sku" => 284758393, "color" => "gray" } ], "fruit" => [ { "name" => "apple", "physical" => { "color" => "red", "shape" => "round" }, "variety" => [ { "name" => "red delicious" }, { "name" => "granny smith" } ] }, { "name" => "banana", "variety" => [ { "name" => "plantain" } ] } ] } end def self.example { 'title' => 'TomlRB Example', 'owner' => { 'name' => 'Tom Preston-Werner', 'organization' => 'GitHub', 'bio' => "GitHub Cofounder & CEO\nLikes tater tots and beer.", 'dob' => Time.utc(1979, 05, 27, 07, 32, 00) }, 'database' => { 'server' => '192.168.1.1', 'ports' => [8001, 8001, 8002], 'connection_max' => 5000, 'enabled' => true }, 'servers' => { 'alpha' => { 'ip' => '10.0.0.1', 'dc' => 'eqdc10' }, 'beta' => { 'ip' => '10.0.0.2', 'dc' => 'eqdc10' } }, 'clients' => { 'data' => [%w(gamma delta), [1, 2]], 'hosts' => %w(alpha omega) }, 'amqp' => { 'exchange' => { 'durable' => true, 'auto_delete' => false } } } end def self.hard_example { 'the' => { 'test_string' => "You'll hate me after this - #", 'hard' => { 'test_array' => ['] ', ' # '], 'test_array2' => ['Test #11 ]proved that', 'Experiment #9 was a success'], 'another_test_string' => ' Same thing, but with a string #', 'harder_test_string' => " And when \"'s are in the string, along with # \"", 'bit#' => { 'what?' => "You don't think some user won't do that?", 'multi_line_array' => [']'] } } }, 'parent' => { 'child1' => { 'key' => 'value' }, 'child2' => [ { 'key2' => 'value' }, { 'key3' => 'value' } ] }, 'a' => { 'b' => [ { 'c' => 3 } ] } } end end toml-rb-1.0.0/test/grammar_test.rb0000644000175000017500000002030613124502322016074 0ustar pravipravi# encoding: utf-8 require_relative 'helper' class GrammarTest < Minitest::Test def test_comment match = TomlRB::Document.parse(' # A comment', root: :comment) assert_equal(nil, match.value) end def test_key match = TomlRB::Document.parse('bad_key-', root: :key) assert_equal('bad_key-', match.value) match = TomlRB::Document.parse('"123.ʎǝʞ.#?"', root: :key) assert_equal('123.ʎǝʞ.#?', match.value) end def test_keygroup indentation_alternatives_for('[akey]') do |str| match = TomlRB::Document.parse(str, root: :keygroup) assert_equal(TomlRB::Keygroup, match.value.class) assert_equal(['akey'], match.value.instance_variable_get('@nested_keys')) end match = TomlRB::Document.parse('[owner.emancu]', root: :keygroup) assert_equal(%w(owner emancu), match.value.instance_variable_get('@nested_keys')) match = TomlRB::Document.parse('["owner.emancu"]', root: :keygroup) assert_equal(%w(owner.emancu), match.value.instance_variable_get('@nested_keys')) match = TomlRB::Document.parse('["first key"."second key"]', root: :keygroup) assert_equal(['first key', 'second key'], match.value.instance_variable_get('@nested_keys')) match = TomlRB::Document.parse('[ owner . emancu ]', root: :keygroup) assert_equal(%w(owner emancu), match.value.instance_variable_get('@nested_keys')) assert_raises Citrus::ParseError do TomlRB::Document.parse('[ owner emancu ]', root: :keygroup) end end def test_keyvalue indentation_alternatives_for('key = "value"') do |str| match = TomlRB::Document.parse(str, root: :keyvalue) assert_equal(TomlRB::Keyvalue, match.value.class) keyvalue = match.value assert_equal('key', keyvalue.instance_variable_get('@key')) assert_equal('value', keyvalue.instance_variable_get('@value')) end end def test_string match = TomlRB::Document.parse('"TomlRB-Example, should work."', root: :string) assert_equal('TomlRB-Example, should work.', match.value) end def test_multiline_string match = TomlRB::Document.parse('"""\tOne\nTwo"""', root: :multiline_string) assert_equal "\tOne\nTwo", match.value to_parse = '"""\ One \ Two\ """' match = TomlRB::Document.parse(to_parse, root: :multiline_string) assert_equal "One Two", match.value end def test_empty_multiline_string to_parse = '""""""' match = TomlRB::Document.parse(to_parse, root: :multiline_string) assert_equal '', match.value end def test_special_characters match = TomlRB::Document.parse('"\0 \" \t \n \r"', root: :string) assert_equal("\0 \" \t \n \r", match.value) match = TomlRB::Document.parse('"C:\\\\Documents\\\\nada.exe"', root: :string) assert_equal('C:\\Documents\\nada.exe', match.value) end def test_bool match = TomlRB::Document.parse('true', root: :bool) assert_equal(true, match.value) match = TomlRB::Document.parse('false', root: :bool) assert_equal(false, match.value) end def test_integer match = TomlRB::Document.parse('26', root: :number) assert_equal(26, match.value) match = TomlRB::Document.parse('1_200_000_999', root: :number) assert_equal(1_200_000_999, match.value) end def test_float match = TomlRB::Document.parse('1.69', root: :number) assert_equal(1.69, match.value) match = TomlRB::Document.parse('1_000.69', root: :number) assert_equal(1000.69, match.value) match = TomlRB::Document.parse('1e6', root: :number) assert_equal(1e6, match.value) match = TomlRB::Document.parse('1.02e-46', root: :number) assert_equal(1.02e-46, match.value) match = TomlRB::Document.parse('+1e4_000_000', root: :number) assert_equal(1e4_000_000, match.value) end def test_signed_numbers match = TomlRB::Document.parse('+26', root: :number) assert_equal(26, match.value) match = TomlRB::Document.parse('-26', root: :number) assert_equal(-26, match.value) match = TomlRB::Document.parse('1.69', root: :number) assert_equal(1.69, match.value) match = TomlRB::Document.parse('-1.69', root: :number) assert_equal(-1.69, match.value) end def test_expressions_with_comments match = TomlRB::Document.parse('[shouldwork] # with comment', root: :keygroup) assert_equal(['shouldwork'], match.value.instance_variable_get('@nested_keys')) match = TomlRB::Document.parse('works = true # with comment', root: :keyvalue).value assert_equal('works', match.instance_variable_get('@key')) assert_equal(true, match.instance_variable_get('@value')) end def test_array match = TomlRB::Document.parse('[]', root: :array) assert_equal([], match.value) match = TomlRB::Document.parse('[ 2, 4]', root: :array) assert_equal([2, 4], match.value) match = TomlRB::Document.parse('[ 2.4, 4.72]', root: :array) assert_equal([2.4, 4.72], match.value) match = TomlRB::Document.parse('[ "hey", "TomlRB"]', root: :array) assert_equal(%w(hey TomlRB), match.value) match = TomlRB::Document.parse('[ ["hey", "TomlRB"], [2,4] ]', root: :array) assert_equal([%w(hey TomlRB), [2, 4]], match.value) match = TomlRB::Document.parse('[ { one = 1 }, { two = 2, three = 3} ]', root: :inline_table_array) assert_equal([{ 'one' => 1 }, { 'two' => 2, 'three' => 3 }], match.value) end def test_empty_array # test that [] is parsed as array and not as inline table array match = TomlRB::Document.parse("a = []", root: :keyvalue).value assert_equal [], match.value end def test_multiline_array multiline_array = "[ \"hey\",\n \"ho\",\n\t \"lets\", \"go\",\n ]" match = TomlRB::Document.parse(multiline_array, root: :array) assert_equal(%w(hey ho lets go), match.value) multiline_array = "[\n#1,\n2,\n# 3\n]" match = TomlRB::Document.parse(multiline_array, root: :array) assert_equal([2], match.value) multiline_array = "[\n# comment\n#, more comments\n4]" match = TomlRB::Document.parse(multiline_array, root: :array) assert_equal([4], match.value) multiline_array = "[\n 1,\n # 2,\n 3 ,\n]" match = TomlRB::Document.parse(multiline_array, root: :array) assert_equal([1, 3], match.value) multiline_array = "[\n 1 , # useless comment\n # 2,\n 3 #other comment\n]" match = TomlRB::Document.parse(multiline_array, root: :array) assert_equal([1, 3], match.value) end # Dates are really hard to test from JSON, due the imposibility to represent # datetimes without quotes. def test_datetime match = TomlRB::Document.parse('1986-08-28T15:15:00Z', root: :datetime) assert_equal(Time.utc(1986, 8, 28, 15, 15), match.value) match = TomlRB::Document.parse('1986-08-28T15:15:00-03:00', root: :datetime) assert_equal(Time.utc(1986, 8, 28, 18, 15), match.value) match = TomlRB::Document.parse('1986-08-28T15:15:00.123-03:00', root: :datetime) assert_equal(Time.utc(1986, 8, 28, 18, 15, 0.123), match.value) match = TomlRB::Document.parse('1986-08-28', root: :datetime) assert_equal(Time.utc(1986, 8, 28, 0, 0, 0), match.value) match = TomlRB::Document.parse('1986-08-28T15:15:00', root: :datetime) assert_equal(Time.utc(1986, 8, 28, 15, 15), match.value) match = TomlRB::Document.parse('1986-08-28T15:15:00.999999', root: :datetime) assert_equal(Time.utc(1986, 8, 28, 15, 15, 0.999999), match.value) end def test_inline_table match = TomlRB::Document.parse('{ }', root: :inline_table) assert_equal({}, match.value.value) match = TomlRB::Document.parse('{ simple = true, params = 2 }', root: :inline_table) assert_equal({ 'simple' => true, 'params' => 2 }, match.value.value) match = TomlRB::Document.parse('{ nest = { really = { hard = true } } }', root: :inline_table) assert_equal({ 'nest' => { 'really' => { 'hard' => true } } }, match.value.value) assert_equal({ nest: { really: { hard: true } } }, match.value.value(true)) end private # Creates all the alternatives of valid indentations to test def indentation_alternatives_for(str) [str, " #{str}", "\t#{str}", "\t\t#{str}"].each do |alternative| yield(alternative) end end end toml-rb-1.0.0/test/helper.rb0000644000175000017500000000007513124502322014667 0ustar pravipravirequire 'minitest/autorun' require_relative '../lib/toml-rb' toml-rb-1.0.0/test/errors_test.rb0000644000175000017500000000507013124502322015763 0ustar pravipravirequire_relative 'helper' class ErrorsTest < Minitest::Test def test_text_after_keygroup str = "[error] if you didn't catch this, your parser is broken" assert_raises(TomlRB::ParseError) { TomlRB.parse(str) } end def test_text_after_string str = 'string = "Anything other than tabs, spaces and newline after a ' str += 'keygroup or key value pair has ended should produce an error ' str += 'unless it is a comment" like this' assert_raises(TomlRB::ParseError) { TomlRB.parse(str) } end def test_multiline_array_bad_string str = <<-EOS array = [ "This might most likely happen in multiline arrays", Like here, "or here, and here" ] End of array comment, forgot the # EOS assert_raises(TomlRB::ParseError) { TomlRB.parse(str) } end def test_multiline_array_string_not_ended str = <<-EOS array = [ "This might most likely happen in multiline arrays", "or here, and here" ] End of array comment, forgot the # EOS assert_raises(TomlRB::ParseError) { TomlRB.parse(str) } end def test_text_after_multiline_array str = <<-EOS array = [ "This might most likely happen in multiline arrays", "or here", "and here" ] End of array comment, forgot the # EOS assert_raises(TomlRB::ParseError) { TomlRB.parse(str) } end def test_text_after_number str = 'number = 3.14 pi <--again forgot the #' assert_raises(TomlRB::ParseError) { TomlRB.parse(str) } end def test_value_overwrite str = "a = 1\na = 2" e = assert_raises(TomlRB::ValueOverwriteError) { TomlRB.parse(str) } assert_equal "Key \"a\" is defined more than once", e.message assert_equal "a", e.key str = "a = false\na = true" assert_raises(TomlRB::ValueOverwriteError) { TomlRB.parse(str) } end def test_table_overwrite str = "[a]\nb=1\n[a]\nc=2" e = assert_raises(TomlRB::ValueOverwriteError) { TomlRB.parse(str) } assert_equal "Key \"a\" is defined more than once", e.message str = "[a]\nb=1\n[a]\nb=1" e = assert_raises(TomlRB::ValueOverwriteError) { TomlRB.parse(str) } assert_equal "Key \"a\" is defined more than once", e.message end def test_value_overwrite_with_table str = "[a]\nb=1\n[a.b]\nc=2" e = assert_raises(TomlRB::ValueOverwriteError) { TomlRB.parse(str) } assert_equal "Key \"b\" is defined more than once", e.message str = "[a]\nb=1\n[a.b.c]\nd=3" e = assert_raises(TomlRB::ValueOverwriteError) { TomlRB.parse(str) } assert_equal "Key \"b\" is defined more than once", e.message end end