toml-rb 0.1.6 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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