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 +4 -4
- data/CHANGELOG.md +5 -0
- data/Gemfile.lock +1 -1
- data/lib/figtree/ip_rules.rb +85 -0
- data/lib/figtree/parser.rb +34 -6
- data/lib/figtree/transformer.rb +66 -58
- data/lib/figtree/version.rb +3 -1
- data/spec/figtree/parser_spec.rb +14 -1
- data/spec/figtree/transformer_spec.rb +66 -54
- data/spec/figtree_spec.rb +7 -1
- data/spec/spec_helper.rb +1 -0
- data/spec/support/wiki_example.ini +10 -0
- metadata +5 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 027349cba07d928b55c539c415a8677994c2ad4f
|
4
|
+
data.tar.gz: 2f16fbdf432e44763f6d07fee7d56f31a96f1b72
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 70cd81c1f602ddb2b8e845958ad005592c663abb8fe5d7b85a69a4db618fea5408b11312013484ec49cd42595f3495869c30a3bf48483acf8af352eab220e0d1
|
7
|
+
data.tar.gz: 3cc856774fb2130c8152e24e25e63b2f5561a9eea951fccffeb2f906c9d752072a3e1e06fc260b43a761c06a3e032810e802eac0389b701eda69119ba6bfb5bf
|
data/CHANGELOG.md
ADDED
data/Gemfile.lock
CHANGED
@@ -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
|
data/lib/figtree/parser.rb
CHANGED
@@ -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(:
|
9
|
-
rule(:
|
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
|
-
|
18
|
-
(
|
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
|
-
|
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
|
-
|
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
|
data/lib/figtree/transformer.rb
CHANGED
@@ -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
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
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
|
data/lib/figtree/version.rb
CHANGED
data/spec/figtree/parser_spec.rb
CHANGED
@@ -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(
|
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
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
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
|
data/spec/figtree_spec.rb
CHANGED
@@ -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.
|
83
|
+
).to be < 0.025
|
84
|
+
# without ip_address parsing this was under 0.014 :(
|
79
85
|
end
|
80
86
|
end
|
81
87
|
|
data/spec/spec_helper.rb
CHANGED
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
|
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:
|