toml-rb 0.1.6 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 97909cd5d9ac3403acf5fba07cfb3177fb343458
4
+ data.tar.gz: ee82bae7cb2a1ec814b269390899ccd47781dabd
5
+ SHA512:
6
+ metadata.gz: ef153da7a2aa9623a2554825b1604f460dfad351bfee4ed4078f2a45b3902992d4ecc2fae2d32b39ed37ef29182d9c4948b389584264e4b499b6f3acad261fd9
7
+ data.tar.gz: 2869e5f5d5b5c946940d3d38cda21440378ba4162cf73c4218aac9c2904630b95e9b075151db1f2fe6449209641f4d65470307b4ac1b4ded510a44e8c0905fdc
data/README.md CHANGED
@@ -1,12 +1,14 @@
1
1
  toml-rb
2
2
  =======
3
3
 
4
- A [TOML](https://github.com/mojombo/toml) parser using [Citrus](http://mjijackson.com/citrus) library.
4
+ [![Gem Version](https://badge.fury.io/rb/toml-rb.svg)](http://badge.fury.io/rb/toml-rb)
5
+ [![Build Status](https://travis-ci.org/emancu/toml-rb.svg)](https://travis-ci.org/emancu/toml-rb)
6
+ [![Code Climate](https://codeclimate.com/github/emancu/toml-rb/badges/gpa.svg)](https://codeclimate.com/github/emancu/toml-rb)
7
+ [![Dependency Status](https://gemnasium.com/emancu/toml-rb.svg)](https://gemnasium.com/emancu/toml-rb)
5
8
 
6
- [![Gem Version](https://badge.fury.io/rb/toml-rb.png)](http://badge.fury.io/rb/toml-rb)
7
- [![Build Status](https://travis-ci.org/emancu/toml-rb.png)](https://travis-ci.org/emancu/toml-rb)
8
- [![Code Climate](https://codeclimate.com/github/emancu/toml-rb.png)](https://codeclimate.com/github/emancu/toml-rb)
9
- [![Dependency Status](https://gemnasium.com/emancu/toml-rb.png)](https://gemnasium.com/emancu/toml-rb)
9
+ A [TOML](https://github.com/mojombo/toml) parser using [Citrus](http://mjackson.github.io/citrus) library.
10
+
11
+ TOML specs supported: `0.3.1`
10
12
 
11
13
  Installation
12
14
  ------------
data/Rakefile CHANGED
@@ -1,6 +1,5 @@
1
1
  task :test do
2
- Dir["test/*_test.rb"].each { |file| load file }
2
+ Dir['test/*_test.rb'].each { |file| load file }
3
3
  end
4
4
 
5
- task :default => :test
6
-
5
+ task default: :test
data/init.rb CHANGED
@@ -3,6 +3,7 @@ require 'citrus'
3
3
  ROOT = File.dirname(File.expand_path(__FILE__))
4
4
 
5
5
  require "#{ROOT}/lib/toml/string"
6
+ require "#{ROOT}/lib/toml/table_array"
6
7
  require "#{ROOT}/lib/toml/keyvalue"
7
8
  require "#{ROOT}/lib/toml/keygroup"
8
9
  require "#{ROOT}/lib/toml/parser"
@@ -12,4 +13,3 @@ Citrus.load "#{ROOT}/lib/toml/grammars/helper.citrus"
12
13
  Citrus.load "#{ROOT}/lib/toml/grammars/primitive.citrus"
13
14
  Citrus.load "#{ROOT}/lib/toml/grammars/array.citrus"
14
15
  Citrus.load "#{ROOT}/lib/toml/grammars/document.citrus"
15
-
@@ -1,7 +1,6 @@
1
1
  require_relative '../init'
2
2
 
3
3
  module TOML
4
-
5
4
  # Public: Returns a hash from *TOML* content.
6
5
  #
7
6
  # content - TOML string to be parsed.
@@ -56,7 +55,6 @@ module TOML
56
55
  TOML.parse(File.read(path), options)
57
56
  end
58
57
 
59
-
60
58
  # Public: Returns a *TOML* string from a Ruby Hash.
61
59
  #
62
60
  # hash - Ruby Hash to be dumped into *TOML*
@@ -5,39 +5,80 @@ module TOML
5
5
  def initialize(hash)
6
6
  @toml_str = ''
7
7
 
8
- visit(hash, '')
8
+ visit(hash, [])
9
9
  end
10
10
 
11
11
  private
12
12
 
13
- def visit(hash, prefix)
13
+ def visit(hash, prefix, extra_brackets = false)
14
14
  nested_pairs = []
15
15
  simple_pairs = []
16
+ table_array_pairs = []
16
17
 
17
18
  hash.keys.sort.each do |key|
18
19
  val = hash[key]
19
- (val.is_a?(Hash) ? nested_pairs : simple_pairs) << [key, val]
20
+ element = [key, val]
21
+
22
+ if val.is_a? Hash
23
+ nested_pairs << element
24
+ elsif val.is_a?(Array) && val.first.is_a?(Hash)
25
+ table_array_pairs << element
26
+ else
27
+ simple_pairs << element
28
+ end
20
29
  end
21
30
 
22
- @toml_str += "[#{prefix}]\n" unless prefix.empty? || simple_pairs.empty?
31
+ unless prefix.empty? || simple_pairs.empty?
32
+ print_prefix prefix, extra_brackets
33
+ end
23
34
 
24
35
  # First add simple pairs, under the prefix
25
36
  simple_pairs.each do |key, val|
26
- @toml_str << "#{key.to_s} = #{to_toml(val)}\n"
37
+ key = quote_key(key) unless bare_key? key
38
+ @toml_str << "#{key} = #{to_toml(val)}\n"
27
39
  end
28
40
 
29
41
  nested_pairs.each do |key, val|
30
- visit(val, prefix.empty? ? key.to_s : [prefix, key].join('.'))
42
+ key = quote_key(key) unless bare_key? key
43
+
44
+ visit val, prefix + [key], false
45
+ end
46
+
47
+ table_array_pairs.each do |key, val|
48
+ key = quote_key(key) unless bare_key? key
49
+ aux_prefix = prefix + [key]
50
+
51
+ val.each do |child|
52
+ if child.empty?
53
+ print_prefix aux_prefix, true
54
+ else
55
+ visit child, aux_prefix, true
56
+ end
57
+ end
31
58
  end
32
59
  end
33
60
 
61
+ def print_prefix(prefix, extra_brackets = false)
62
+ new_prefix = prefix.join('.')
63
+ new_prefix = '[' + new_prefix + ']' if extra_brackets
64
+
65
+ @toml_str += "[" + new_prefix + "]\n"
66
+ end
67
+
34
68
  def to_toml(obj)
35
- case
36
- when obj.is_a?(Time)
69
+ if obj.is_a? Time
37
70
  obj.strftime('%Y-%m-%dT%H:%M:%SZ')
38
71
  else
39
72
  obj.inspect
40
73
  end
41
74
  end
75
+
76
+ def bare_key?(key)
77
+ !!key.to_s.match(/^[a-zA-Z0-9_-]*$/)
78
+ end
79
+
80
+ def quote_key(key)
81
+ '"' + key.gsub('"', '\\"') + '"'
82
+ end
42
83
  end
43
84
  end
@@ -6,30 +6,30 @@ grammar TomlArray
6
6
  end
7
7
 
8
8
  rule elements
9
- float_array | string_array | array_array | integer_array | datetime_array | bool_array
9
+ comment | float_array | string_array | array_array | integer_array | datetime_array | bool_array
10
10
  end
11
11
 
12
12
  rule float_array
13
- (float ("," indent? float)*)
13
+ (float ("," indent? (float | comment))*)
14
14
  end
15
15
 
16
16
  rule string_array
17
- (string ("," indent? string)*)
17
+ (string ("," indent? (string | comment))*)
18
18
  end
19
19
 
20
20
  rule array_array
21
- (array ("," indent? array)*)
21
+ (array ("," indent? (array | comment))*)
22
22
  end
23
23
 
24
24
  rule integer_array
25
- (integer ("," indent? integer)*)
25
+ (integer ("," indent? (integer | comment))*)
26
26
  end
27
27
 
28
28
  rule datetime_array
29
- (datetime ("," indent? datetime)*)
29
+ (datetime ("," indent? (datetime | comment))*)
30
30
  end
31
31
 
32
32
  rule bool_array
33
- (bool ("," indent? bool)*)
33
+ (bool ("," indent? (bool | comment))*)
34
34
  end
35
35
  end
@@ -3,11 +3,15 @@ grammar Document
3
3
  include TomlArray
4
4
 
5
5
  rule document
6
- (comment | keygroup | keyvalue | line_break)*
6
+ (comment | table_array | keygroup | keyvalue | line_break)*
7
+ end
8
+
9
+ rule table_array
10
+ (space? '[[' (space? key space? "."?)+ ']]' space? comment?) <TableArray>
7
11
  end
8
12
 
9
13
  rule keygroup
10
- (space? '[' nested_keys:(key "."?)+ ']' space? comment?) <Keygroup>
14
+ (space? '[' (space? key space? "."?)+ ']' space? comment?) <Keygroup>
11
15
  end
12
16
 
13
17
  rule keyvalue
@@ -5,30 +5,72 @@ grammar Primitive
5
5
  string | bool | datetime | number
6
6
  end
7
7
 
8
+ ##
9
+ # String rules
10
+ ##
11
+
8
12
  rule string
9
- (/(["'])(?:\\?.)*?\1/ space) <TomlString>
13
+ multiline_string | multiline_literal | basic_string | literal_string
10
14
  end
11
15
 
12
- rule bool
13
- true | false
16
+ rule basic_string
17
+ (/(["])(?:\\?.)*?\1/ space) <TomlBasicString>
18
+ end
19
+
20
+ rule literal_string
21
+ (/(['])(?:\\?.)*?\1/ space) <TomlLiteralString>
22
+ end
23
+
24
+ rule multiline_string
25
+ ('"""' line_break* text:~'"""' '"""' space) <TomlMultilineString>
26
+ end
27
+
28
+ rule multiline_literal
29
+ ("'''" line_break* text:~"'''" "'''" space) <TomlMultilineLiteral>
14
30
  end
15
31
 
16
- # Full Zulu form
32
+ ##
33
+ # Date time rules
34
+ ##
35
+
17
36
  rule datetime
18
- (y:/\d\d\d\d/ "-" m:/\d\d/ "-" d:/\d\d/ "T" h:/\d\d/ ":" mi:/\d\d/ ":" s:/\d\d/ "Z") {
19
- y,m,d,h,mi,s = [:y,:m,:d,:h,:mi,:s].map{|s| capture(s) }
20
- Time.utc(*[y,m,d,h,mi,s].map(&:value))
37
+ (date_skeleton ("Z" | date_offset | [,\.] frac:(/\d/1*6) date_offset)) {
38
+ skeleton = captures[:date_skeleton].first
39
+ y,m,d,h,mi,s = skeleton.value
40
+ offset = captures[:date_offset].first || "+00:00"
41
+ sec_frac = captures[:frac].first || 0
42
+ s = "#{s}.#{sec_frac}".to_f
43
+
44
+ Time.new(*[y,m,d,h,mi,s,offset.to_s])
45
+ }
46
+ end
47
+
48
+ rule date_skeleton
49
+ (y:/\d\d\d\d/ "-" m:/\d\d/ "-" d:/\d\d/ "T" h:/\d\d/ ":" mi:/\d\d/ ":" s:/\d\d/) {
50
+ [:y,:m,:d,:h,:mi,:s].map{ |s| capture(s).value }
21
51
  }
22
52
  end
23
53
 
54
+ rule date_offset
55
+ offset:(sign /\d\d/ ":" /\d\d/)
56
+ end
57
+
58
+ ##
59
+ # Number rules
60
+ ##
61
+
24
62
  rule number
25
- float | integer
63
+ exponent | float | integer
26
64
  end
27
65
 
28
66
  rule float
29
67
  (integer '.' integer) { to_str.to_f }
30
68
  end
31
69
 
70
+ rule exponent
71
+ ( (float | integer) [eE] integer) { to_str.to_f }
72
+ end
73
+
32
74
  rule integer
33
75
  (sign? [0-9]+) { to_str.to_i }
34
76
  end
@@ -37,8 +79,12 @@ grammar Primitive
37
79
  '+' | '-'
38
80
  end
39
81
 
40
- rule key
41
- [a-zA-Z_] [a-zA-Z_0-9#?]*
82
+ ##
83
+ # Boolean rules
84
+ ##
85
+
86
+ rule bool
87
+ true | false
42
88
  end
43
89
 
44
90
  rule true
@@ -48,4 +94,20 @@ grammar Primitive
48
94
  rule false
49
95
  'false' { false }
50
96
  end
97
+
98
+ ##
99
+ # Key rules
100
+ ##
101
+
102
+ rule key
103
+ quoted_key | bare_key
104
+ end
105
+
106
+ rule bare_key
107
+ [a-zA-Z_] [a-zA-Z_0-9_-]*
108
+ end
109
+
110
+ rule quoted_key
111
+ string
112
+ end
51
113
  end
@@ -8,14 +8,15 @@ module TOML
8
8
  @nested_keys.each do |key|
9
9
  key = symbolize_keys ? key.to_sym : key
10
10
  hash[key] = {} unless hash[key]
11
- hash = hash[key]
11
+ element = hash[key]
12
+ hash = element.is_a?(Array) ? element.last : element
12
13
  end
13
14
 
14
15
  hash
15
16
  end
16
17
 
17
18
  def accept_visitor(parser)
18
- parser.visit_keygroup(self)
19
+ parser.visit_keygroup self
19
20
  end
20
21
  end
21
22
  end
@@ -23,6 +24,6 @@ end
23
24
  # Used in document.citrus
24
25
  module Keygroup
25
26
  def value
26
- TOML::Keygroup.new(capture(:nested_keys).to_str.split('.'))
27
+ TOML::Keygroup.new(captures[:key].map(&:value))
27
28
  end
28
29
  end
@@ -8,7 +8,7 @@ module TOML
8
8
 
9
9
  def assign(hash, symbolize_keys = false)
10
10
  key = symbolize_keys ? @key.to_sym : @key
11
- raise ValueOverwriteError if hash[key]
11
+ fail ValueOverwriteError if hash[key]
12
12
  hash[key] = @value
13
13
  end
14
14
 
@@ -19,6 +19,10 @@ module TOML
19
19
 
20
20
  # Read about the Visitor pattern
21
21
  # http://en.wikipedia.org/wiki/Visitor_pattern
22
+ def visit_table_array(table_array)
23
+ @current = table_array.navigate_keys @hash, @symbolize_keys
24
+ end
25
+
22
26
  def visit_keygroup(keygroup)
23
27
  @current = keygroup.navigate_keys(@hash, @symbolize_keys)
24
28
  end
@@ -1,27 +1,43 @@
1
1
  # Used in primitive.citrus
2
- module TomlString
2
+ module TomlBasicString
3
3
  def value
4
- aux = first.value
5
- s = 0
6
- o = []
7
- while s < aux.length
8
- if aux[s] == '\\'
9
- s += 1
10
- case aux[s]
11
- when 't' then o << "\t"
12
- when 'n' then o << "\n"
13
- when '\\' then o << '\\'
14
- when '"' then o << '"'
15
- when 'r' then o << "\r"
16
- when '0' then o << "\0"
17
- else
18
- o << '\\' << aux[s]
19
- end
20
- else
21
- o << aux[s]
22
- end
23
- s += 1
24
- end
25
- o[1...-1].join
4
+ aux = TomlBasicString.transform_escaped_chars first.value
5
+
6
+ aux[1...-1]
7
+ end
8
+
9
+ def self.transform_escaped_chars(str)
10
+ str
11
+ .gsub(/\\0/, "\0")
12
+ .gsub(/\\t/, "\t")
13
+ .gsub(/\\n/, "\n")
14
+ .gsub(/\\\"/, '"')
15
+ .gsub(/\\r/, "\r")
16
+ .gsub(/\\\\/, '\\')
17
+ end
18
+ end
19
+
20
+ module TomlLiteralString
21
+ def value
22
+ first.value[1...-1]
23
+ end
24
+ end
25
+
26
+ module TomlMultilineString
27
+ def value
28
+ aux = captures[:text].first.value
29
+
30
+ # Remove spaces on multilined Singleline strings
31
+ aux.gsub!(/\\\r?\n[\n\t\r ]*/, '')
32
+
33
+ TomlBasicString.transform_escaped_chars aux
34
+ end
35
+ end
36
+
37
+ module TomlMultilineLiteral
38
+ def value
39
+ aux = captures[:text].first.value
40
+
41
+ aux.gsub(/\\\r?\n[\n\t\r ]*/, '')
26
42
  end
27
43
  end
@@ -0,0 +1,29 @@
1
+ module TOML
2
+ class TableArray
3
+ def initialize(nested_keys)
4
+ @nested_keys = nested_keys
5
+ end
6
+
7
+ def navigate_keys(hash, symbolize_keys = false)
8
+ @nested_keys.each do |key|
9
+ key = symbolize_keys ? key.to_sym : key
10
+ hash[key] = [] unless hash[key]
11
+ hash[key] << {} if @nested_keys.last == key.to_s || hash[key].empty?
12
+ hash = hash[key].last
13
+ end
14
+
15
+ hash
16
+ end
17
+
18
+ def accept_visitor(parser)
19
+ parser.visit_table_array self
20
+ end
21
+ end
22
+ end
23
+
24
+ # Used in document.citrus
25
+ module TableArray
26
+ def value
27
+ TOML::TableArray.new(captures[:key].map(&:value))
28
+ end
29
+ end