figtree 1.0.1 → 1.2.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: b6b5da9f31e285ace11997fa8066b4fb9926ad3a
4
- data.tar.gz: 1c11bdf776b084922e35c782e9737d3275f32ddd
3
+ metadata.gz: 027349cba07d928b55c539c415a8677994c2ad4f
4
+ data.tar.gz: 2f16fbdf432e44763f6d07fee7d56f31a96f1b72
5
5
  SHA512:
6
- metadata.gz: b515887114e72ca380c854619e5596e2027d7f120c372ff1c2e48b03d35a6bcd50cfa792dac15c435ac7a7639844bab810b95a7813bba4bbffc2e722653c80b6
7
- data.tar.gz: 7ccf9fb5da3a512f019c37652ed680f64b502beee938974a560a7b2c25dd5e8bfd1a6a8d414787e3df354a17038d3a705907c61c9709da6b5e64c465372e5c75
6
+ metadata.gz: 70cd81c1f602ddb2b8e845958ad005592c663abb8fe5d7b85a69a4db618fea5408b11312013484ec49cd42595f3495869c30a3bf48483acf8af352eab220e0d1
7
+ data.tar.gz: 3cc856774fb2130c8152e24e25e63b2f5561a9eea951fccffeb2f906c9d752072a3e1e06fc260b43a761c06a3e032810e802eac0389b701eda69119ba6bfb5bf
@@ -0,0 +1,5 @@
1
+ - Version 0.0.1: initial set up
2
+ - Version 0.0.2: some bug fixes, overrides
3
+ - Version 1.0.0: moving away from #load_conifg to just #new
4
+ - Version 1.0.1: refactoring in IniConfig
5
+ - Version 1.2.0: adding support for pre-group comments and ip addresses
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- figtree (1.0.0)
4
+ figtree (1.2.0)
5
5
  parslet (~> 1.7)
6
6
  wannabe_bool (~> 0.2)
7
7
 
@@ -0,0 +1,85 @@
1
+ # from https://github.com/kschiess/parslet/blob/master/example/ip_address.rb
2
+ module Figtree
3
+ # Must be used in concert with IPv4
4
+ module IPv6
5
+ include Parslet
6
+
7
+ rule(:colon) { str(':') }
8
+ rule(:dcolon) { colon >> colon }
9
+
10
+ # h16 :
11
+ def h16r(times)
12
+ (h16 >> colon).repeat(times, times)
13
+ end
14
+
15
+ # : h16
16
+ def h16l(times)
17
+ (colon >> h16).repeat(0,times)
18
+ end
19
+
20
+ # A 128-bit IPv6 address is divided into eight 16-bit pieces. Each piece is
21
+ # represented numerically in case-insensitive hexadecimal, using one to four
22
+ # hexadecimal digits (leading zeroes are permitted). The eight encoded
23
+ # pieces are given most-significant first, separated by colon characters.
24
+ # Optionally, the least-significant two pieces may instead be represented in
25
+ # IPv4 address textual format. A sequence of one or more consecutive
26
+ # zero-valued 16-bit pieces within the address may be elided, omitting all
27
+ # their digits and leaving exactly two consecutive colons in their place to
28
+ # mark the elision.
29
+ rule(:ipv6) {
30
+ (
31
+ (
32
+ h16r(6) |
33
+ dcolon >> h16r(5) |
34
+ h16.maybe >> dcolon >> h16r(4) |
35
+ (h16 >> h16l(1)).maybe >> dcolon >> h16r(3) |
36
+ (h16 >> h16l(2)).maybe >> dcolon >> h16r(2) |
37
+ (h16 >> h16l(3)).maybe >> dcolon >> h16r(1) |
38
+ (h16 >> h16l(4)).maybe >> dcolon
39
+ ) >> ls32 |
40
+ (h16 >> h16l(5)).maybe >> dcolon >> h16 |
41
+ (h16 >> h16l(6)).maybe >> dcolon
42
+ ).as(:ipv6)
43
+ }
44
+
45
+ rule(:h16) {
46
+ hexdigit.repeat(1,4)
47
+ }
48
+
49
+ rule(:ls32) {
50
+ (h16 >> colon >> h16) |
51
+ ipv4
52
+ }
53
+
54
+ rule(:hexdigit) {
55
+ digit | match("[a-fA-F]")
56
+ }
57
+ end
58
+
59
+ module IPv4
60
+ include Parslet
61
+
62
+ # A host identified by an IPv4 literal address is represented in
63
+ # dotted-decimal notation (a sequence of four decimal numbers in the range 0
64
+ # to 255, separated by "."), as described in [RFC1123] by reference to
65
+ # [RFC0952]. Note that other forms of dotted notation may be interpreted on
66
+ # some platforms, as described in Section 7.4, but only the dotted-decimal
67
+ # form of four octets is allowed by this grammar.
68
+ rule(:ipv4) {
69
+ (dec_octet >> str('.') >> dec_octet >> str('.') >>
70
+ dec_octet >> str('.') >> dec_octet).as(:ipv4)
71
+ }
72
+
73
+ rule(:dec_octet) {
74
+ str('25') >> match("[0-5]") |
75
+ str('2') >> match("[0-4]") >> digit |
76
+ str('1') >> digit >> digit |
77
+ match('[1-9]') >> digit |
78
+ digit
79
+ }
80
+
81
+ rule(:digit) {
82
+ match('[0-9]')
83
+ }
84
+ end
85
+ end
@@ -1,21 +1,29 @@
1
1
  require 'parslet'
2
+ require 'figtree/ip_rules'
2
3
 
3
4
  module Figtree
4
5
  # ConFIG into a Tree :)
5
6
  class Parser < Parslet::Parser
7
+ include IPv4
8
+ include IPv6
9
+
6
10
  rule(:eof) { any.absent? }
7
11
  rule(:group_title) { match('[a-zA-Z]').repeat(1) }
8
- rule(:newline) { match("\\n").repeat(1) >> match("\\r").maybe }
9
- rule(:space) { match('\s').repeat(0) }
12
+ rule(:space) { match("\s").repeat(0) }
13
+ rule(:newline) { match("\n") >> match("\r").maybe }
14
+
10
15
  rule(:grouper) do
16
+ newline.maybe >>
11
17
  str('[') >>
12
18
  group_title.as(:group_title) >>
13
19
  str(']')
14
20
  end
15
21
 
16
22
  rule(:comment) do
17
- str(';') >>
18
- (newline.absent? >> any).repeat
23
+ # comments go uncaptured
24
+ (str(';') >>
25
+ (newline.absent? >> any).repeat) >>
26
+ newline.maybe
19
27
  end
20
28
 
21
29
  rule(:string) do
@@ -25,6 +33,7 @@ module Figtree
25
33
  end
26
34
 
27
35
  rule(:boolean) do
36
+ # expand this check
28
37
  (str('no') | str('yes')).as(:boolean)
29
38
  end
30
39
 
@@ -32,6 +41,10 @@ module Figtree
32
41
  match('[0-9]').repeat(1).as(:number)
33
42
  end
34
43
 
44
+ rule(:ip_address) do
45
+ (ipv4 | ipv6).as(:ip_address)
46
+ end
47
+
35
48
  rule(:array) do
36
49
  (match('[a-zA-Z]').repeat(1) >>
37
50
  (str(',') >>
@@ -59,7 +72,16 @@ module Figtree
59
72
  space >>
60
73
  str("=") >>
61
74
  space >>
62
- (number | boolean | array | snake_case_key | file_path | string)
75
+ # this ordering matters
76
+ # we are roughly moving from more
77
+ # to less specific
78
+ (ip_address |
79
+ number |
80
+ boolean |
81
+ array |
82
+ snake_case_key |
83
+ file_path |
84
+ string)
63
85
  end
64
86
 
65
87
  rule(:override_assignment) do
@@ -86,6 +108,12 @@ module Figtree
86
108
  repeat.maybe
87
109
  end
88
110
 
89
- root(:group)
111
+ rule(:comment_or_group) do
112
+ comment.maybe >>
113
+ newline.maybe >>
114
+ group.maybe
115
+ end
116
+
117
+ root(:comment_or_group)
90
118
  end
91
119
  end
@@ -1,66 +1,74 @@
1
1
  require 'parslet'
2
2
  require 'ostruct'
3
+ require 'ipaddr'
3
4
  require 'wannabe_bool'
4
5
 
5
6
  module Figtree
6
- # a transformer takes a parsed, valid AST and applies rules, usually
7
- # in a context free manner
8
- class Transformer < Parslet::Transform
9
- # TODO these could largely be consolidated with some rearrangement
10
- # these are all type conversions
11
- rule(:snake_case_key => simple(:key), :number => simple(:value)) do
12
- {
13
- key.to_sym => Integer(value)
14
- }
15
- end
16
- rule(:snake_case_key => simple(:key), :string => simple(:value)) do
17
- {
18
- key.to_sym => String(value)
19
- }
20
- end
21
- rule(:snake_case_key => simple(:key), :file_path => simple(:value)) do
22
- {
23
- key.to_sym => String(value)
24
- }
25
- end
26
- # depends on wannabe_bool refining String class
27
- rule(:snake_case_key => simple(:key), :boolean => simple(:value)) do
28
- {
29
- key.to_sym => String(value).to_b
30
- }
31
- end
32
- rule(:snake_case_key => simple(:key), :array => simple(:value)) do
33
- {
34
- key.to_sym => String(value).split(",")
35
- }
36
- 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
+ # 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
37
38
 
38
- # ini files are trees of a fixed height, if the file handle is the root
39
- # subgroups are its children, and subgroup members are the next level of children
40
- rule(:group => subtree(:group_members)) do
41
- group_title = group_members[0][:group_title].to_sym
42
- group_values = Subgroup.new(group_members[1..-1].reduce({}, :merge!))
43
- {
44
- group_title => group_values
45
- }
46
- end
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
47
45
 
48
- # where does overrides come from? an argument into #apply on
49
- # Transformer, that allows an additional capture outside the AST
50
- # to be added to the context of the transform
51
- rule(
52
- :key_to_be_overridden => subtree(:overridden_key),
53
- :optional_key => subtree(:overriding_key),
54
- :file_path => subtree(:new_file_path),
55
- ) do
56
- if override.to_sym == overriding_key[:snake_case_key].to_sym
57
- {
58
- overridden_key[:snake_case_key] => String(new_file_path)
59
- }
60
- else
61
- {
62
- }
63
- end
64
- end
65
- end
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
55
+
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
66
74
  end
@@ -1,3 +1,5 @@
1
1
  module Figtree
2
- VERSION = "1.0.1"
2
+ # for reference see
3
+ # http://guides.rubygems.org/patterns/#semantic-versioning
4
+ VERSION = "1.2.0"
3
5
  end
@@ -10,7 +10,13 @@ module Figtree
10
10
  expect(parser.grouper).to parse('[common]')
11
11
  end
12
12
  it 'can parse comments' do
13
- expect(parser.comment).to parse('; This is a comment\n')
13
+ expect(parser.comment).to parse("; This is a comment\n")
14
+ expect(parser.comment).to parse("; last modified 1 April 2001 by John Doe\n")
15
+ comment_first = File.open('spec/support/wiki_example.ini', &:readline)
16
+ expect(parser.comment).to parse(comment_first)
17
+ end
18
+ it 'can parse comments then groups' do
19
+ expect(parser.comment_or_group).to parse("; comment\n[groop]\nassignment = present")
14
20
  end
15
21
  it 'can parse snake_case keys' do
16
22
  expect(parser.snake_case_key).to parse('basic_size_limit')
@@ -29,6 +35,13 @@ module Figtree
29
35
  it 'can parse numbers' do
30
36
  expect(parser.number).to parse("26214400")
31
37
  end
38
+ it 'can parse ip addresses' do
39
+ expect(parser.ip_address).to parse("FE80:0000:0000:0000:0202:B3FF:FE1E:8329")
40
+ expect(parser.ip_address).to parse('111.222.3.4')
41
+ expect(parser.ip_address).to parse('192.0.2.62')
42
+ expect(parser.ip_address).to_not parse('f11.222.3.4')
43
+ expect(parser.ip_address).to_not parse('111.222.3')
44
+ end
32
45
  it 'can parse booleans flexibly' do
33
46
  expect(parser.boolean).to parse("no")
34
47
  expect(parser.boolean).to parse("yes")
@@ -1,58 +1,70 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  module Figtree
4
- describe Transformer do
5
- context "can do type conversion" do
6
- let(:int_tree) do
7
- Parser.new.parse("[common]\nbasic_size_limit = 26214400\n")
8
- end
9
- let(:arr_tree) do
10
- Parser.new.parse("[http]\nparams = array,of,values\n")
11
- end
12
- let(:bool_tree) do
13
- Parser.new.parse("[ftp]\nenabled = no\n")
14
- end
15
- it 'can apply an int type conversion' do
16
- expect(Transformer.new.apply(int_tree)).to eq(
17
- [
18
- {
19
- common: Subgroup.new({basic_size_limit: 26214400})
20
- }
21
- ]
22
- )
23
- end
24
- it 'can apply an array type conversion' do
25
- expect(Transformer.new.apply(arr_tree)).to eq(
26
- [
27
- {
28
- http: Subgroup.new(params: ["array", "of", "values"])
29
- }
30
- ]
31
- )
32
- end
33
- it 'can apply a bool type conversion' do
34
- expect(Transformer.new.apply(bool_tree)).to eq(
35
- [
36
- {
37
- ftp: Subgroup.new(enabled: false)
38
- }
39
- ]
40
- )
41
- end
42
- end
43
- context "overrides by angle brackets" do
44
- let(:override_tree) do
45
- Parser.new.parse("[http]\npath = /srv/\npath<production> = /srv/var/tmp/\n")
46
- end
47
- it 'can apply an override' do
48
- expect(Transformer.new.apply(override_tree, override: :production)).to eq(
49
- [
50
- {
51
- http: Subgroup.new(path: '/srv/var/tmp/')
52
- }
53
- ]
54
- )
55
- end
56
- end
57
- end
4
+ describe Transformer do
5
+ context "can do type conversion" do
6
+ let(:int_tree) do
7
+ Parser.new.parse("[common]\nbasic_size_limit = 26214400\n")
8
+ end
9
+ let(:arr_tree) do
10
+ Parser.new.parse("[http]\nparams = array,of,values\n")
11
+ end
12
+ let(:bool_tree) do
13
+ Parser.new.parse("[ftp]\nenabled = no\n")
14
+ end
15
+ let(:ip_tree) do
16
+ Parser.new.parse("[database]\nserver=192.0.2.62")
17
+ end
18
+ it 'can apply an int type conversion' do
19
+ expect(Transformer.new.apply(int_tree)).to eq(
20
+ [
21
+ {
22
+ common: Subgroup.new({basic_size_limit: 26214400})
23
+ }
24
+ ]
25
+ )
26
+ end
27
+ it 'can apply an array type conversion' do
28
+ expect(Transformer.new.apply(arr_tree)).to eq(
29
+ [
30
+ {
31
+ http: Subgroup.new(params: ["array", "of", "values"])
32
+ }
33
+ ]
34
+ )
35
+ end
36
+ it 'can apply a bool type conversion' do
37
+ expect(Transformer.new.apply(bool_tree)).to eq(
38
+ [
39
+ {
40
+ ftp: Subgroup.new(enabled: false)
41
+ }
42
+ ]
43
+ )
44
+ end
45
+ it 'it can apply an ip address type conversion' do
46
+ expect(Transformer.new.apply(ip_tree)).to eq(
47
+ [
48
+ {
49
+ database: Subgroup.new(server: IPAddr.new("192.0.2.62"))
50
+ }
51
+ ]
52
+ )
53
+ end
54
+ end
55
+ context "overrides by angle brackets" do
56
+ let(:override_tree) do
57
+ Parser.new.parse("[http]\npath = /srv/\npath<production> = /srv/var/tmp/\n")
58
+ end
59
+ it 'can apply an override' do
60
+ expect(Transformer.new.apply(override_tree, override: :production)).to eq(
61
+ [
62
+ {
63
+ http: Subgroup.new(path: '/srv/var/tmp/')
64
+ }
65
+ ]
66
+ )
67
+ end
68
+ end
69
+ end
58
70
  end
@@ -69,13 +69,19 @@ describe Figtree do
69
69
  expect(Figtree::IniConfig.new(settings_path)).to eq(the_whole_kebab)
70
70
  end
71
71
 
72
+ it 'can parse the wiki example' do
73
+ wiki_example = Figtree::IniConfig.new('spec/support/wiki_example.ini')
74
+ expect(wiki_example.database.server).to_not be_nil
75
+ end
76
+
72
77
  context "performance" do
73
78
  it 'can parse the whole ini file quickly' do
74
79
  expect(
75
80
  Benchmark.realtime do
76
81
  Figtree::IniConfig.new(settings_path)
77
82
  end
78
- ).to be < 0.014
83
+ ).to be < 0.025
84
+ # without ip_address parsing this was under 0.014 :(
79
85
  end
80
86
  end
81
87
 
@@ -5,6 +5,7 @@ require 'pry'
5
5
  require 'ostruct'
6
6
  require 'parslet'
7
7
  require 'parslet/rig/rspec'
8
+ require 'parslet/convenience'
8
9
 
9
10
  require 'simplecov'
10
11
  SimpleCov.start
@@ -0,0 +1,10 @@
1
+ ; last modified 1 April 2001 by John Doe
2
+ [owner]
3
+ name="John Doe"
4
+ organization="Acme Widgets Inc."
5
+
6
+ [database]
7
+ ; use IP address in case network name resolution is not working
8
+ server=192.0.2.62
9
+ port=143
10
+ file="payroll.dat"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: figtree
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Moore-Niemi
@@ -94,6 +94,7 @@ extra_rdoc_files: []
94
94
  files:
95
95
  - ".gitignore"
96
96
  - ".ruby-version"
97
+ - CHANGELOG.md
97
98
  - Gemfile
98
99
  - Gemfile.lock
99
100
  - MIT-LICENSE
@@ -101,6 +102,7 @@ files:
101
102
  - figtree.gemspec
102
103
  - lib/figtree.rb
103
104
  - lib/figtree/ini_config.rb
105
+ - lib/figtree/ip_rules.rb
104
106
  - lib/figtree/parser.rb
105
107
  - lib/figtree/transformer.rb
106
108
  - lib/figtree/version.rb
@@ -111,6 +113,7 @@ files:
111
113
  - spec/support/settings.conf
112
114
  - spec/support/unparseable_settings.conf
113
115
  - spec/support/untransformable_settings.conf
116
+ - spec/support/wiki_example.ini
114
117
  homepage: https://github.com/mooreniemi/figtree
115
118
  licenses:
116
119
  - MIT
@@ -144,4 +147,5 @@ test_files:
144
147
  - spec/support/settings.conf
145
148
  - spec/support/unparseable_settings.conf
146
149
  - spec/support/untransformable_settings.conf
150
+ - spec/support/wiki_example.ini
147
151
  has_rdoc: