figtree 1.0.1 → 1.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.
- 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:
|