figtree 1.2.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 027349cba07d928b55c539c415a8677994c2ad4f
4
- data.tar.gz: 2f16fbdf432e44763f6d07fee7d56f31a96f1b72
3
+ metadata.gz: 027e03ceb0e9b227e731efdacc239459ee0a344b
4
+ data.tar.gz: cda00334893c5bbc8c7d2c299ad91f35c6df4859
5
5
  SHA512:
6
- metadata.gz: 70cd81c1f602ddb2b8e845958ad005592c663abb8fe5d7b85a69a4db618fea5408b11312013484ec49cd42595f3495869c30a3bf48483acf8af352eab220e0d1
7
- data.tar.gz: 3cc856774fb2130c8152e24e25e63b2f5561a9eea951fccffeb2f906c9d752072a3e1e06fc260b43a761c06a3e032810e802eac0389b701eda69119ba6bfb5bf
6
+ metadata.gz: 76e628f7ef15ff4474d8f2bc09adfa02b2c6a315607b68b2c3879a1b12e1938022e05b0a82aecccc70ab7c47e011109cc4eb46de9c8d5dcbc353a03b310e8979
7
+ data.tar.gz: a6552300f61d0d71020667b41a01e37ecc9bdc09f4b87888eef1b547d496fa2ced5783a4f5cff170da637a1e7e51dfc939a88ba8b8ae7b66e03fae5fc61a00c4
data/README.md CHANGED
@@ -11,10 +11,17 @@
11
11
 
12
12
  # Figtree
13
13
  ## about
14
- A parser and transformer for loading `.ini` files into Ruby dot notation accessible objects. `.ini` is not a standardized format. But the parser and transformer are easy to extend, unlike regex. :) And it's at 100% LOC coverage.
14
+ A parser and transformer for loading `.ini` files into Ruby dot notation accessible objects. `.ini` is not a standardized format. But the parser and transformer are easy to extend.
15
+
16
+ The goal of figtree is not to accept all `.ini` files generously, but more strictly define `.ini` files so we can make smarter guesses about how to covert your settings into objects.
17
+
18
+ What kinds of objects are supported? Currently we can recognize [unix style filepaths into Pathname](http://ruby-doc.org/stdlib-2.0.0/libdoc/pathname/rdoc/Pathname.html), [ip4 and ip6 addresses into IPAddr](http://ruby-doc.org/stdlib-2.0.0/libdoc/ipaddr/rdoc/IPAddr.html), and most common Ruby types (String, Array, Boolean, Integer). If there's other types you'd like to see supported, please [file an issue](https://github.com/mooreniemi/figtree/issues/new).
15
19
 
16
20
  If the `.ini` file is invalid, an error will be raised, with the line and char position of the error. If you extend this gem to have more rules, and one of those rules fails to transform, you will have an error raised.
17
21
 
22
+ ## alternatives
23
+ If you want an industrial strength, pure Ruby solution, check out [inifile gem](https://github.com/TwP/inifile). It is much looser about what it accepts as valid `.ini` files, and with no pesky dependencies! If you want to see exactly which cases `figtree` supports vs `inifile`, compare our spec files. Many of the cases in our `invalid/` folder `inifile` will parse for you.
24
+
18
25
  ## disambiguation
19
26
  Looking for the graphical viewer of phyllogenic trees? You want this other [Figtree](http://tree.bio.ed.ac.uk/software/figtree/).
20
27
 
@@ -43,13 +50,15 @@ A typical `.ini` file takes slightly less than 0.02s to be parsed, transformed,
43
50
  => "/srv/var/tmp/"
44
51
 
45
52
  ## development
46
- ### installation
47
- `bundle install`
53
+ ### helpers
54
+ `bin/setup`
55
+ `bin/console`
56
+
57
+ While in console, you can use `parse_ini_named name` and as long as it's in `spec/support/name.ini` it'll parse it for you with helpful error output. Sometimes this is a faster feedback mechanism than going through the tests.
48
58
 
49
59
  ### tests
50
60
  `rspec spec/`
51
61
 
52
62
  #### TODO
53
- - move way from needing to parse whole file at once? (move to `IO.foreach` ?)
63
+ - more cleanup
54
64
  - give char/line position of transformer failures
55
- - refactor marked TODO listings in files (mostly refactoring to generic in Transformer)
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "figtree"
5
+
6
+ require "parslet/convenience"
7
+
8
+ parser = Figtree::Parser.new
9
+
10
+ require "pry"
11
+ command_set = Pry::CommandSet.new do
12
+ command "parse_ini_named", "", :keep_retval => true do |name|
13
+ parser = Figtree::Parser.new
14
+ ini_as_string = File.read("spec/support/#{name}.ini")
15
+ parser.parse_with_debug(ini_as_string)
16
+ end
17
+ end
18
+
19
+ #Pry.start parser, :commands => command_set
20
+ Pry.start nil, :commands => command_set
@@ -0,0 +1,8 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
8
+
@@ -20,9 +20,13 @@ module Figtree
20
20
 
21
21
  def figgy_parse(str)
22
22
  Parser.new.parse(str)
23
- rescue Parslet::ParseFailed => failure
23
+ # argument error is invalid byte sequence
24
+ rescue Parslet::ParseFailed, ArgumentError => failure
25
+ if failure.class == Parslet::ParseFailed
26
+ failure = failure.cause.ascii_tree
27
+ end
24
28
  STDERR.puts "\nInvalid ini file.\n" +
25
- "Error: #{failure.cause.ascii_tree}" +
29
+ "Error: #{failure}" +
26
30
  "Please correct the file and retry."
27
31
  raise
28
32
  end
@@ -8,33 +8,73 @@ module Figtree
8
8
  include IPv6
9
9
 
10
10
  rule(:eof) { any.absent? }
11
- rule(:group_title) { match('[a-zA-Z]').repeat(1) }
12
- rule(:space) { match("\s").repeat(0) }
13
- rule(:newline) { match("\n") >> match("\r").maybe }
11
+ rule(:group_title) { match('[a-zA-Z_]').repeat(1) }
12
+ rule(:space) { (match("\s") | str(' ')) }
13
+ rule(:spaces) { (space.repeat(2) | comment) }
14
+ rule(:newline) { str("\n") >> match("\r").maybe }
15
+ rule(:terminator) do
16
+ space.repeat(0) >> (comment | newline | eof)
17
+ end
18
+ rule(:backslash) do
19
+ space.repeat(0) >> str("\\")
20
+ end
14
21
 
15
22
  rule(:grouper) do
16
- newline.maybe >>
17
23
  str('[') >>
18
24
  group_title.as(:group_title) >>
19
25
  str(']')
20
26
  end
21
27
 
28
+ rule(:comment_start) { (str(';') | str('#')) }
29
+ rule(:comment_end) { (newline | eof) }
22
30
  rule(:comment) do
23
- # comments go uncaptured
24
- (str(';') >>
25
- (newline.absent? >> any).repeat) >>
26
- newline.maybe
31
+ (
32
+ comment_start >>
33
+ space.repeat(0) >>
34
+ (
35
+ comment_end.absent? >> any
36
+ ).repeat
37
+ ) >>
38
+ space.repeat(0) >>
39
+ comment_end
40
+ end
41
+
42
+ rule(:quoted_string) do
43
+ str('"') >>
44
+ (
45
+ (str('\\') >> any) | (str('"').absent? >> any)
46
+ ).repeat(1) >>
47
+ str('"')
48
+ end
49
+
50
+
51
+ rule(:unquoted_string) do
52
+ (
53
+ (
54
+ (
55
+ (backslash | terminator).absent?
56
+ ) >> any
57
+ ).repeat(1).as(:left) >>
58
+ backslash >>
59
+ terminator
60
+ ).repeat(0) >>
61
+ (
62
+ terminator.absent? >> any
63
+ ).repeat(1).as(:right) >>
64
+ terminator
27
65
  end
28
66
 
29
67
  rule(:string) do
30
- str('"') >>
31
- ((str('\\') >> any) | (str('"').absent? >> any)).repeat.as(:string) >>
32
- str('"')
68
+ (quoted_string | unquoted_string).as(:string)
33
69
  end
34
70
 
35
71
  rule(:boolean) do
36
- # expand this check
37
- (str('no') | str('yes')).as(:boolean)
72
+ (
73
+ str('no') |
74
+ str('yes') |
75
+ str('false') |
76
+ str('true')
77
+ ).as(:boolean)
38
78
  end
39
79
 
40
80
  rule(:number) do
@@ -45,19 +85,36 @@ module Figtree
45
85
  (ipv4 | ipv6).as(:ip_address)
46
86
  end
47
87
 
88
+ rule(:at_least_one_char) do
89
+ match('[a-zA-Z]').repeat(1)
90
+ end
91
+
48
92
  rule(:array) do
49
- (match('[a-zA-Z]').repeat(1) >>
50
- (str(',') >>
51
- match('[a-zA-Z]').repeat(1)).repeat.maybe).maybe.as(:array) >>
52
- (str(',') | newline | eof)
93
+ (
94
+ # minimum array
95
+ at_least_one_char >>
96
+ (
97
+ # extending elementwise
98
+ str(',') >> space.repeat.maybe >>
99
+ at_least_one_char
100
+ ).repeat(1)
101
+ ).as(:array) >>
102
+ (newline | eof)
53
103
  end
54
104
 
55
105
  rule(:file_path) do
56
- match('[/a-z/]').repeat(1).as(:file_path)
106
+ (
107
+ (
108
+ str('/') >>
109
+ at_least_one_char
110
+ ).repeat(1) >>
111
+ str('/').maybe
112
+ ).as(:file_path)
57
113
  end
58
114
 
59
115
  rule(:snake_case_key) do
60
- match('[a-zA-Z0-9_]').repeat(1).as(:snake_case_key)
116
+ match('[a-zA-Z0-9_]').repeat(1).
117
+ as(:snake_case_key)
61
118
  end
62
119
 
63
120
  rule(:snakey_option_key) do
@@ -67,29 +124,36 @@ module Figtree
67
124
  str('>')
68
125
  end
69
126
 
70
- rule(:assignment) do
71
- snake_case_key >>
72
- space >>
73
- str("=") >>
74
- space >>
127
+ rule(:value) do
75
128
  # this ordering matters
76
129
  # we are roughly moving from more
77
130
  # to less specific
78
- (ip_address |
79
- number |
80
- boolean |
81
- array |
82
- snake_case_key |
83
- file_path |
84
- string)
131
+ (
132
+ ip_address |
133
+ number |
134
+ boolean |
135
+ array |
136
+ file_path |
137
+ string
138
+ )
139
+ end
140
+
141
+ rule(:equals_value) do
142
+ space.repeat(0) >>
143
+ str("=") >>
144
+ space.repeat(0) >>
145
+ value >>
146
+ newline.repeat(0)
147
+ end
148
+
149
+ rule(:assignment) do
150
+ snake_case_key >>
151
+ equals_value
85
152
  end
86
153
 
87
154
  rule(:override_assignment) do
88
155
  snakey_option_key >>
89
- space >>
90
- str("=") >>
91
- space >>
92
- file_path
156
+ equals_value
93
157
  end
94
158
 
95
159
  rule(:assignment_or_comment) do
@@ -97,21 +161,27 @@ module Figtree
97
161
  end
98
162
 
99
163
  rule(:group_member) do
100
- newline.maybe >>
101
164
  assignment_or_comment >>
102
- newline.maybe
165
+ space.repeat(0) >>
166
+ newline.repeat(0)
103
167
  end
104
168
 
105
169
  rule(:group) do
106
- (grouper >>
107
- group_member.repeat.maybe).as(:group).
108
- repeat.maybe
170
+ (
171
+ grouper >>
172
+ space.repeat(0) >>
173
+ comment.maybe >>
174
+ newline.repeat(0) >>
175
+ group_member.repeat(0)
176
+ ).as(:group).
177
+ repeat(0)
109
178
  end
110
179
 
111
180
  rule(:comment_or_group) do
112
- comment.maybe >>
113
- newline.maybe >>
114
- group.maybe
181
+ # may start file with attribution
182
+ # comment or timestamp etc
183
+ comment.repeat.maybe >>
184
+ group
115
185
  end
116
186
 
117
187
  root(:comment_or_group)
@@ -4,71 +4,84 @@ require 'ipaddr'
4
4
  require 'wannabe_bool'
5
5
 
6
6
  module Figtree
7
- # a transformer takes a parsed, valid AST and applies rules, usually
8
- # in a context free manner
9
- class Transformer < Parslet::Transform
10
- # TODO these could largely be consolidated with some rearrangement
11
- # these are all type conversions
12
- rule(:snake_case_key => simple(:key), :number => simple(:value)) do
13
- {
14
- key.to_sym => Integer(value)
15
- }
16
- end
17
- rule(:snake_case_key => simple(:key), :string => simple(:value)) do
18
- {
19
- key.to_sym => String(value)
20
- }
21
- end
22
- rule(:snake_case_key => simple(:key), :file_path => simple(:value)) do
23
- {
24
- key.to_sym => String(value)
25
- }
26
- end
27
- # depends on wannabe_bool refining String class
28
- rule(:snake_case_key => simple(:key), :boolean => simple(:value)) do
29
- {
30
- key.to_sym => String(value).to_b
31
- }
32
- end
33
- rule(:snake_case_key => simple(:key), :array => simple(:value)) do
34
- {
35
- key.to_sym => String(value).split(",")
36
- }
37
- end
7
+ # a transformer takes a parsed, valid AST and applies rules, usually
8
+ # in a context free manner
9
+ class Transformer < Parslet::Transform
10
+ rule(:snake_case_key => simple(:key), :number => simple(:value)) do
11
+ {
12
+ key.to_sym => Integer(value)
13
+ }
14
+ end
15
+ rule(:snake_case_key => simple(:key), :string => subtree(:value)) do
16
+ merged_string =
17
+ case value
18
+ when Hash
19
+ value[:right]
20
+ when Array
21
+ value.inject("") do |string, element|
22
+ if !element[:left].nil?
23
+ string + element[:left]
24
+ else
25
+ string + element[:right]
26
+ end
27
+ end
28
+ else
29
+ value
30
+ end
31
+ {
32
+ # remove whitespace after cast
33
+ key.to_sym => String(merged_string).strip
34
+ }
35
+ end
36
+ rule(:snake_case_key => simple(:key), :file_path => simple(:value)) do
37
+ {
38
+ key.to_sym => Pathname.new(value)
39
+ }
40
+ end
41
+ # depends on wannabe_bool refining String class
42
+ rule(:snake_case_key => simple(:key), :boolean => simple(:value)) do
43
+ {
44
+ key.to_sym => String(value).to_b
45
+ }
46
+ end
47
+ rule(:snake_case_key => simple(:key), :array => simple(:value)) do
48
+ {
49
+ key.to_sym => String(value).split(",")
50
+ }
51
+ end
38
52
 
39
- rule(:snake_case_key => simple(:key), :ip_address => subtree(:value)) do
40
- # right now we're not distinguishing what kind of ip it is
41
- {
42
- key.to_sym => IPAddr.new((value.values.first.to_s))
43
- }
44
- end
53
+ rule(:snake_case_key => simple(:key), :ip_address => subtree(:value)) do
54
+ {
55
+ key.to_sym => IPAddr.new((value.values.first.to_s))
56
+ }
57
+ end
45
58
 
46
- # ini files are trees of a fixed height, if the file handle is the root
47
- # subgroups are its children, and subgroup members are the next level of children
48
- rule(:group => subtree(:group_members)) do
49
- group_title = group_members[0][:group_title].to_sym
50
- group_values = Subgroup.new(group_members[1..-1].reduce({}, :merge!))
51
- {
52
- group_title => group_values
53
- }
54
- end
59
+ # ini files are trees of a fixed height, if the file handle is the root
60
+ # subgroups are its children, and subgroup members are the next level of children
61
+ rule(:group => subtree(:group_members)) do
62
+ group_title = group_members[0][:group_title].to_sym
63
+ group_values = Subgroup.new(group_members[1..-1].reduce({}, :merge!))
64
+ {
65
+ group_title => group_values
66
+ }
67
+ end
55
68
 
56
- # where does overrides come from? an argument into #apply on
57
- # Transformer, that allows an additional capture outside the AST
58
- # to be added to the context of the transform
59
- rule(
60
- :key_to_be_overridden => subtree(:overridden_key),
61
- :optional_key => subtree(:overriding_key),
62
- :file_path => subtree(:new_file_path),
63
- ) do
64
- if override.to_sym == overriding_key[:snake_case_key].to_sym
65
- {
66
- overridden_key[:snake_case_key] => String(new_file_path)
67
- }
68
- else
69
- {
70
- }
71
- end
72
- end
73
- end
69
+ # where does overrides come from? an argument into #apply on
70
+ # Transformer, that allows an additional capture outside the AST
71
+ # to be added to the context of the transform
72
+ rule(
73
+ :key_to_be_overridden => subtree(:overridden_key),
74
+ :optional_key => subtree(:overriding_key),
75
+ :file_path => subtree(:new_file_path),
76
+ ) do
77
+ if override.to_sym == overriding_key[:snake_case_key].to_sym
78
+ {
79
+ overridden_key[:snake_case_key] => Pathname.new(new_file_path)
80
+ }
81
+ else
82
+ {
83
+ }
84
+ end
85
+ end
86
+ end
74
87
  end
@@ -1,5 +1,5 @@
1
1
  module Figtree
2
2
  # for reference see
3
3
  # http://guides.rubygems.org/patterns/#semantic-versioning
4
- VERSION = "1.2.0"
4
+ VERSION = "2.0.0"
5
5
  end
@@ -3,79 +3,154 @@ require 'spec_helper'
3
3
  module Figtree
4
4
  describe Parser do
5
5
  let(:parser) { Parser.new }
6
+ let(:string) do
7
+ "a bb ccc dddd eeeee ffffff\n"
8
+ end
9
+
6
10
  it 'can parse newlines' do
7
11
  expect(parser.newline).to parse("\n")
12
+ expect(parser.newline).to_not parse("\\n")
13
+ expect(parser.newline).to_not parse("\\\n")
14
+ end
15
+ it 'terminates correctly' do
16
+ expect(parser.terminator).to parse(" #comment")
17
+ expect(parser.terminator).to parse(" #comment\n")
18
+ expect(parser.terminator).to parse(" \n")
19
+ expect(parser.terminator).to parse(" ")
20
+ expect(parser.unquoted_string).to parse("f #ffoo")
21
+ expect(parser.unquoted_string).to parse("f #ffoo\n")
8
22
  end
9
23
  it 'can parse group names' do
10
24
  expect(parser.grouper).to parse('[common]')
25
+ expect(parser.grouper).to parse('[common_also]')
11
26
  end
12
27
  it 'can parse comments' do
13
28
  expect(parser.comment).to parse("; This is a comment\n")
29
+ expect(parser.comment).to parse("# This is also a comment\n")
14
30
  expect(parser.comment).to parse("; last modified 1 April 2001 by John Doe\n")
31
+ expect(parser.comment).to parse("#comment \\n")
15
32
  comment_first = File.open('spec/support/wiki_example.ini', &:readline)
16
33
  expect(parser.comment).to parse(comment_first)
17
34
  end
18
- it 'can parse comments then groups' do
19
- expect(parser.comment_or_group).to parse("; comment\n[groop]\nassignment = present")
20
- end
21
35
  it 'can parse snake_case keys' do
22
36
  expect(parser.snake_case_key).to parse('basic_size_limit')
23
37
  end
24
- it 'can parse strings' do
25
- expect(parser.string).to parse('"hello there, ftp uploading"')
38
+
39
+ describe 'strings of all kinds' do
40
+ it 'can parse spaces' do
41
+ expect(parser.spaces).to parse("#foo")
42
+ expect(parser.spaces).to parse("# foo")
43
+ expect(parser.spaces).to parse("# foo\n")
44
+ expect(parser.spaces).to_not parse(" ")
45
+ expect(parser.spaces).to_not parse("\s")
46
+ expect(parser.spaces).to_not parse("a b")
47
+ end
48
+ it 'can parse strings' do
49
+ expect(parser.string).to parse('"hello there, ftp uploading"')
50
+ end
51
+ it 'can parse unquoted strings' do
52
+ expect(parser.unquoted_string).to parse(string)
53
+ expect(parser.unquoted_string).to parse("multiline \\\nsupport\n")
54
+ end
55
+ it 'can parse multiline with comment' do
56
+ expect(parser.unquoted_string).to parse("a #comment\n")
57
+ expect(parser.unquoted_string).to parse("a #")
58
+ expect(parser.unquoted_string).to_not parse("a\nb")
59
+ expect(parser.unquoted_string).to parse("a \\#comment\n b\n")
60
+ expect(parser.assignment).to parse("foo = a \\nb\n")
61
+ expect(parser.assignment).to parse("foo = a \\ # and here, too\nb\n")
62
+
63
+ group = "[section_three]\nthree = hello \\\nmultiline\nother = 2"
64
+ expect(parser.group).to parse(group)
65
+
66
+ multiline_only = File.read('spec/support/multiline_only.ini')
67
+ expect(parser.group).to parse(multiline_only)
68
+ end
26
69
  end
70
+
27
71
  it 'can parse arrays' do
28
- expect(parser.array).to parse("a,")
72
+ expect(parser.array).to_not parse(',,')
73
+ expect(parser.array).to_not parse("a\n")
74
+ expect(parser.array).to_not parse("a,")
75
+ expect(parser.array).to parse("a,b\n")
29
76
  expect(parser.array).to parse("a,b")
30
77
  expect(parser.array).to parse("a,b,c\n")
31
- expect(parser.array).to_not parse(',,')
78
+ expect(parser.array).to parse("words, with, spaces, after\n")
79
+ expect(parser.array).to parse("several,diff,words,only,nonumbers\n")
32
80
  expect(parser.array).to parse("array,of,values\n")
33
81
  expect(parser.array).to parse("array,of,values")
34
82
  end
83
+
35
84
  it 'can parse numbers' do
36
85
  expect(parser.number).to parse("26214400")
37
86
  end
87
+
38
88
  it 'can parse ip addresses' do
39
89
  expect(parser.ip_address).to parse("FE80:0000:0000:0000:0202:B3FF:FE1E:8329")
40
90
  expect(parser.ip_address).to parse('111.222.3.4')
41
- expect(parser.ip_address).to parse('192.0.2.62')
91
+ expect(parser.ip_address).to parse('192.0.2.62')
42
92
  expect(parser.ip_address).to_not parse('f11.222.3.4')
43
93
  expect(parser.ip_address).to_not parse('111.222.3')
44
94
  end
45
95
  it 'can parse booleans flexibly' do
46
96
  expect(parser.boolean).to parse("no")
47
97
  expect(parser.boolean).to parse("yes")
48
- end
49
- it 'can parse assignments' do
50
- expect(parser.assignment).to parse('basic_size_limit = 26214400')
51
- expect(parser.assignment).to parse('path = /srv/var/tmp/')
52
- expect(parser.assignment).to_not parse('path<itscript> = /srv/tmp/')
53
- expect(parser.assignment).to parse('name = "hello there, ftp uploading"')
54
- expect(parser.assignment).to parse('params = array,of,values')
55
- end
56
- it 'can parse keys with optional overrides' do
57
- expect(parser.snakey_option_key).to parse('path<itscript>')
98
+ expect(parser.boolean).to parse("true")
99
+ expect(parser.boolean).to parse("false")
58
100
  end
59
101
  it 'can parse file_paths' do
60
102
  expect(parser.file_path).to parse('/srv/tmp/')
103
+ expect(parser.file_path).to_not parse(string)
61
104
  end
62
- it 'can parse overrides' do
63
- expect(parser.override_assignment).to parse('path<itscript> = /srv/tmp/')
105
+
106
+ describe 'overriding stuff' do
107
+ it 'can parse keys with optional overrides' do
108
+ expect(parser.snakey_option_key).to parse('path<itscript>')
109
+ end
110
+ it 'can parse overrides' do
111
+ expect(parser.override_assignment).to parse('path<itscript> = /srv/tmp/')
112
+ end
64
113
  end
65
114
 
66
- describe "using the settings.conf file for input" do
115
+ describe "building up from root, checking group, etc" do
67
116
  let(:settings_path) { 'spec/support/settings.conf' }
68
117
  let(:multi_group) {
69
118
  [
70
119
  "[common]",
71
120
  "basic_size_limit = 234234",
72
121
  "[rare]",
73
- "pepes = 0"
122
+ "pepes = 0",
123
+ "and = feels guy"
74
124
  ].join("\n") + "\n"
75
125
  }
76
126
 
127
+ it 'can parse assignments' do
128
+ expect(parser.assignment).to parse('basic_size_limit = 26214400')
129
+ expect(parser.assignment).to parse('path = /srv/var/tmp/')
130
+ expect(parser.assignment).to_not parse('path<itscript> = /srv/tmp/')
131
+ expect(parser.assignment).to parse('name = "hello there, ftp uploading"')
132
+ expect(parser.assignment).to parse('params = array,of,values')
133
+ expect(parser.assignment).to parse("hostname = My Computer\n")
134
+ end
135
+ it 'can parse comments then groups' do
136
+ expect(parser.comment_or_group).to parse("; comment\n[groop]\nassignment = 0\n")
137
+ end
138
+ it 'parses comment or group' do
139
+ expect(parser.comment_or_group).to parse("[database]\nserver = 192.0.2.62")
140
+ end
77
141
  it 'parses a group member' do
78
- expect(parser.group_member).to parse("\nbasic_size_limit = 26214400\n")
142
+ expect(parser.group_member).to parse("basic_size_limit = 26214400\n")
143
+ end
144
+ it 'can parse group members with inline comments' do
145
+ group_with_comments = "[section_two] # you can comment here" +
146
+ "\none = 42 # and even here!"
147
+ expect(parser.group).to parse(group_with_comments)
148
+ end
149
+ it 'can parse assignment irrespective of spacing' do
150
+ expect(parser.assignment).
151
+ to parse("basic_size_limit= 26214400\n")
152
+ expect(parser.assignment).
153
+ to parse("basic_size_limit = 26214400\n")
79
154
  end
80
155
  it 'can parse single assignment inside a group' do
81
156
  expect(parser.group).
@@ -85,6 +160,10 @@ module Figtree
85
160
  expect(parser.group).
86
161
  to parse("[common]\nbasic_size_limit = 26214400\nstudent_size_limit = 52428800\n")
87
162
  end
163
+ it 'can parse values including strings' do
164
+ group_member = "hostname = #{string}"
165
+ expect(parser.group_member).to parse(group_member)
166
+ end
88
167
  it 'can parse multiple groups' do
89
168
  expect(parser.group).to parse(multi_group)
90
169
  end
@@ -12,8 +12,12 @@ module Figtree
12
12
  let(:bool_tree) do
13
13
  Parser.new.parse("[ftp]\nenabled = no\n")
14
14
  end
15
+ let(:string_tree) do
16
+ Parser.new.parse_with_debug("[database]\nserver = is here\n")
17
+ Parser.new.parse("[database]\nserver = is here\n")
18
+ end
15
19
  let(:ip_tree) do
16
- Parser.new.parse("[database]\nserver=192.0.2.62")
20
+ Parser.new.parse("[database]\nserver = 192.0.2.62")
17
21
  end
18
22
  it 'can apply an int type conversion' do
19
23
  expect(Transformer.new.apply(int_tree)).to eq(
@@ -42,6 +46,15 @@ module Figtree
42
46
  ]
43
47
  )
44
48
  end
49
+ it 'it can apply a string type conversion' do
50
+ expect(Transformer.new.apply(string_tree)).to eq(
51
+ [
52
+ {
53
+ database: Subgroup.new(server: "is here")
54
+ }
55
+ ]
56
+ )
57
+ end
45
58
  it 'it can apply an ip address type conversion' do
46
59
  expect(Transformer.new.apply(ip_tree)).to eq(
47
60
  [
@@ -60,7 +73,7 @@ module Figtree
60
73
  expect(Transformer.new.apply(override_tree, override: :production)).to eq(
61
74
  [
62
75
  {
63
- http: Subgroup.new(path: '/srv/var/tmp/')
76
+ http: Subgroup.new(path: Pathname.new('/srv/var/tmp/'))
64
77
  }
65
78
  ]
66
79
  )
@@ -1,6 +1,4 @@
1
1
  require 'spec_helper'
2
- # for the performance test at bottom
3
- require 'benchmark'
4
2
 
5
3
  describe Figtree do
6
4
  describe '#load_config' do
@@ -10,7 +8,7 @@ describe Figtree do
10
8
  :basic_size_limit => 26214400,
11
9
  :student_size_limit => 52428800,
12
10
  :paid_users_size_limit => 2147483648,
13
- :path => "/srv/var/tmp/",
11
+ :path => Pathname.new("/srv/var/tmp/"),
14
12
  )
15
13
  end
16
14
 
@@ -19,7 +17,7 @@ describe Figtree do
19
17
  :basic_size_limit => 26214400,
20
18
  :student_size_limit => 52428800,
21
19
  :paid_users_size_limit => 2147483648,
22
- :path => "/srv/tmp/",
20
+ :path => Pathname.new("/srv/tmp/"),
23
21
  )
24
22
  end
25
23
 
@@ -32,15 +30,15 @@ describe Figtree do
32
30
  :basic_size_limit => 26214400,
33
31
  :student_size_limit=> 52428800,
34
32
  :paid_users_size_limit=> 2147483648,
35
- :path=> "/srv/var/tmp/"
33
+ :path=> Pathname.new("/srv/var/tmp/")
36
34
  }
37
35
  )
38
36
  },
39
37
  {
40
38
  ftp: Figtree::Subgroup.new(
41
39
  {
42
- :name => "hello there, ftp uploading",
43
- :path => "/tmp/",
40
+ :name => "\"hello there, ftp uploading\"",
41
+ :path => Pathname.new("/tmp/"),
44
42
  :enabled => false
45
43
  }
46
44
  )
@@ -48,8 +46,8 @@ describe Figtree do
48
46
  {
49
47
  http: Figtree::Subgroup.new(
50
48
  {
51
- :name => "http uploading",
52
- :path => "/tmp/",
49
+ :name => "\"http uploading\"",
50
+ :path => Pathname.new("/tmp/"),
53
51
  :params => ["array", "of", "values"]
54
52
  }
55
53
  )
@@ -70,24 +68,68 @@ describe Figtree do
70
68
  end
71
69
 
72
70
  it 'can parse the wiki example' do
73
- wiki_example = Figtree::IniConfig.new('spec/support/wiki_example.ini')
71
+ wiki_example = Figtree::IniConfig.new('spec/support/wiki_example.ini')
74
72
  expect(wiki_example.database.server).to_not be_nil
75
73
  end
76
74
 
77
75
  context "performance" do
76
+ require 'benchmark'
78
77
  it 'can parse the whole ini file quickly' do
79
78
  expect(
80
79
  Benchmark.realtime do
81
80
  Figtree::IniConfig.new(settings_path)
82
81
  end
83
82
  ).to be < 0.025
84
- # without ip_address parsing this was under 0.014 :(
83
+ # without ip_address parsing this was under 0.014 :(
84
+ end
85
+ end
86
+
87
+ context "checking full transform" do
88
+ let(:mini_ini_file) { 'spec/support/mini_ini_example.ini' }
89
+ let(:mini_ini_config) do
90
+ Figtree::IniConfig.new(
91
+ {
92
+ Network: Figtree::Subgroup.new(
93
+ hostname: "My Computer",
94
+ address: "dhcp",
95
+ dns: IPAddr.new("192.168.1.1")
96
+ )
97
+ }
98
+ )
99
+ end
100
+
101
+ it 'parses unquoted strings properly' do
102
+ expect(Figtree::IniConfig.new(mini_ini_file)).
103
+ to eq(mini_ini_config)
104
+ end
105
+ end
106
+
107
+ context "comparing to inifile gem" do
108
+ it 'does not parse anything INIFile gem cant parse' do
109
+ bad_ini_files = Dir["spec/support/invalid/*.ini"]
110
+ bad_ini_files.each do |ini_file|
111
+ #puts "\nfile is: #{ini_file}"
112
+ expect{ Figtree::IniConfig.new("#{ini_file}") }.
113
+ to raise_error
114
+ end
115
+ end
116
+ it 'can parse a subset of what INIFile gem can parse' do
117
+ ini_files = Dir["spec/support/*.ini"]
118
+ ini_files.each do |ini_file|
119
+ #puts "\nfile is: #{ini_file}"
120
+ expect(Figtree::IniConfig.new("#{ini_file}")).
121
+ to be_a Figtree::IniConfig
122
+ end
85
123
  end
86
124
  end
87
125
 
88
126
  context "invalid ini file" do
89
- let(:unparseable_config) { 'spec/support/unparseable_settings.conf' }
90
- let(:untransformable_config) { 'spec/support/untransformable_settings.conf' }
127
+ let(:unparseable_config) do
128
+ 'spec/support/invalid/unparseable_settings.conf'
129
+ end
130
+ let(:untransformable_config) do
131
+ 'spec/support/invalid/untransformable_settings.conf'
132
+ end
91
133
  it 'throws ParseFailed if unparseable' do
92
134
  expect { Figtree::IniConfig.new(unparseable_config) }.
93
135
  to raise_error(Parslet::ParseFailed)
@@ -14,6 +14,10 @@ Dir[File.join(File.dirname(__FILE__), "..", "lib" , "**.rb")].each do |file|
14
14
  require file
15
15
  end
16
16
 
17
+ # because we raise more than one error type from invalid
18
+ # this should prob be avoided though
19
+ RSpec::Expectations.configuration.warn_about_potential_false_positives = false
20
+
17
21
  RSpec.configure do |config|
18
22
  # quiet output from figtree_spec invalid ini context
19
23
  config.before { allow($stderr).to receive(:puts) }
@@ -0,0 +1,10 @@
1
+ # comments should be ignored
2
+ [section_one]
3
+ one = 1
4
+ two = 2
5
+
6
+ [section_two] # you can comment here
7
+ one = 42 # and even here!
8
+ multi = a \ # and here, too
9
+ b
10
+
@@ -0,0 +1,18 @@
1
+ [section_one]
2
+ one = 1
3
+ two = 2
4
+
5
+ [section_two]
6
+ three = "-3"
7
+ multi = multiline \
8
+ support
9
+
10
+ ; comments should be ignored
11
+ [section_three]
12
+ four =true
13
+ five=false # comments can go here
14
+ six = "6.0" ; and here, too
15
+
16
+ [section_five]
17
+ seven_and_eight= "7 & 8"
18
+
@@ -0,0 +1,6 @@
1
+ [section_one]
2
+ one = 1
3
+ two = 2
4
+
5
+ ; the following is not a valid line
6
+ invalid line
@@ -0,0 +1,11 @@
1
+ [section_one]
2
+ one = 1
3
+ two = 2
4
+
5
+ [section_two]
6
+ close-quote = "some text without
7
+ a closing quote - should
8
+ be interesting
9
+
10
+ [section_three]
11
+ wat = where is that closing quote?
@@ -0,0 +1,5 @@
1
+
2
+ ;just the offending entry
3
+ [www.substancia.com AutoHTTPAgent (ver *)]
4
+ Parent=Version Checkers
5
+ Browser=Subst�ncia
@@ -0,0 +1,6 @@
1
+ [section_one]
2
+ one = 1
3
+ two = 2
4
+
5
+ [section_two]
6
+ end-of-file = here is the last value \
@@ -0,0 +1,13 @@
1
+ ; this test file demonstrates escape sequences supported by IniFile
2
+ [normal]
3
+ foo = http://en.wikipedia.org/wiki/Foobar
4
+
5
+ [escaped]
6
+ tabs = There is a tab\tcharacter in here somewhere
7
+ carriage return = Who uses these anyways?\r
8
+ newline = Trust newline!\nAlways there when you need him.\nSplittin' those lines.
9
+ null = Who'd be silly enough to put\0 a null character in the middle of a string? \
10
+ Stroustrup would not approve!
11
+ backslash = This string \\t contains \\n no \\r special \\0 characters!
12
+ quoted = "Escaping works\tinside quoted strings!"
13
+
@@ -0,0 +1,3 @@
1
+ ; without any section, a default 'global' section will be created
2
+ one = 1
3
+ two = 2
@@ -0,0 +1,5 @@
1
+ [section_one]
2
+ one = 3
3
+
4
+ [section_five]
5
+ five=5
@@ -0,0 +1,4 @@
1
+ [Network]
2
+ hostname = My Computer
3
+ address = dhcp
4
+ dns = 192.168.1.1
@@ -0,0 +1,7 @@
1
+ # comments should be ignored
2
+ ; multiple comments characters are supported
3
+ ; (I'm lookin' at you, samba)
4
+ [section_one]
5
+ one = 1
6
+ two = 2
7
+
@@ -0,0 +1,23 @@
1
+ [section_one]
2
+ one = 1
3
+ two = 2
4
+
5
+ [section_two]
6
+ three = 3
7
+
8
+ ; comments should be ignored
9
+ [section_three]
10
+ three = hello \
11
+ multiline
12
+ other = "stuff"
13
+
14
+ [section_four]
15
+ four = hello \ # comments work here, too
16
+ multiple \ # and here !!!
17
+ multilines # and even here (OMG)
18
+ five = "multiple lines
19
+ inside of quotations
20
+ preserve everything"
21
+
22
+ [empty_lines]
23
+ not_empty=full
@@ -0,0 +1,5 @@
1
+ [section_three]
2
+ two = 2
3
+ three = hello \
4
+ multiline
5
+ other = "stuff"
@@ -0,0 +1,5 @@
1
+ ; comments should be ignored
2
+ [section_one]
3
+ one = 1
4
+ two=2
5
+
@@ -0,0 +1,4 @@
1
+ ; section headers can be values
2
+ [section_one]
3
+ one=[value]
4
+ two=2
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: figtree
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Moore-Niemi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-10 00:00:00.000000000 Z
11
+ date: 2015-10-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parslet
@@ -88,7 +88,9 @@ dependencies:
88
88
  version: '0.10'
89
89
  description: See README.md
90
90
  email: moore.niemi@gmail.com
91
- executables: []
91
+ executables:
92
+ - console
93
+ - setup
92
94
  extensions: []
93
95
  extra_rdoc_files: []
94
96
  files:
@@ -99,6 +101,8 @@ files:
99
101
  - Gemfile.lock
100
102
  - MIT-LICENSE
101
103
  - README.md
104
+ - bin/console
105
+ - bin/setup
102
106
  - figtree.gemspec
103
107
  - lib/figtree.rb
104
108
  - lib/figtree/ini_config.rb
@@ -110,9 +114,24 @@ files:
110
114
  - spec/figtree/transformer_spec.rb
111
115
  - spec/figtree_spec.rb
112
116
  - spec/spec_helper.rb
117
+ - spec/support/comment.ini
118
+ - spec/support/good.ini
119
+ - spec/support/invalid/bad_1.ini
120
+ - spec/support/invalid/bad_2.ini
121
+ - spec/support/invalid/browscap.ini
122
+ - spec/support/invalid/continuation.ini
123
+ - spec/support/invalid/escape.ini
124
+ - spec/support/invalid/global.ini
125
+ - spec/support/invalid/unparseable_settings.conf
126
+ - spec/support/invalid/untransformable_settings.conf
127
+ - spec/support/merge.ini
128
+ - spec/support/mini_ini_example.ini
129
+ - spec/support/mixed_comment.ini
130
+ - spec/support/multiline.ini
131
+ - spec/support/multiline_only.ini
132
+ - spec/support/param.ini
133
+ - spec/support/section.ini
113
134
  - spec/support/settings.conf
114
- - spec/support/unparseable_settings.conf
115
- - spec/support/untransformable_settings.conf
116
135
  - spec/support/wiki_example.ini
117
136
  homepage: https://github.com/mooreniemi/figtree
118
137
  licenses:
@@ -144,8 +163,23 @@ test_files:
144
163
  - spec/figtree/transformer_spec.rb
145
164
  - spec/figtree_spec.rb
146
165
  - spec/spec_helper.rb
166
+ - spec/support/comment.ini
167
+ - spec/support/good.ini
168
+ - spec/support/invalid/bad_1.ini
169
+ - spec/support/invalid/bad_2.ini
170
+ - spec/support/invalid/browscap.ini
171
+ - spec/support/invalid/continuation.ini
172
+ - spec/support/invalid/escape.ini
173
+ - spec/support/invalid/global.ini
174
+ - spec/support/invalid/unparseable_settings.conf
175
+ - spec/support/invalid/untransformable_settings.conf
176
+ - spec/support/merge.ini
177
+ - spec/support/mini_ini_example.ini
178
+ - spec/support/mixed_comment.ini
179
+ - spec/support/multiline.ini
180
+ - spec/support/multiline_only.ini
181
+ - spec/support/param.ini
182
+ - spec/support/section.ini
147
183
  - spec/support/settings.conf
148
- - spec/support/unparseable_settings.conf
149
- - spec/support/untransformable_settings.conf
150
184
  - spec/support/wiki_example.ini
151
185
  has_rdoc: