figtree 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1233d1fd9829365dc29dc2ed44b73634c7c4cfa6
4
+ data.tar.gz: 44fba04a7b4c08a88f1c4d20e0464d0d25643ab8
5
+ SHA512:
6
+ metadata.gz: f0ff4a80575eeea798dfe2a2adeee750f614428d866f160a3bb4f7cae001f797e9aedf671496102b18b67738f2f29e4464d2ee02d4a6882759ec852b30355c13
7
+ data.tar.gz: d5c9d8a28065b65c74a976a47e5a67215418fa8ba8437a289db1f83a12b08355c9ca7f00596cf29709873af0c31537c049dba6d97fa2526443545c38bf36a5d6
data/.gitignore ADDED
@@ -0,0 +1,38 @@
1
+ .DS_STORE
2
+
3
+ *.gem
4
+ *.rbc
5
+ /.config
6
+ /coverage/
7
+ /InstalledFiles
8
+ /pkg/
9
+ /spec/reports/
10
+ /spec/examples.txt
11
+ /test/tmp/
12
+ /test/version_tmp/
13
+ /tmp/
14
+
15
+ ## Specific to RubyMotion:
16
+ .dat*
17
+ .repl_history
18
+ build/
19
+
20
+ ## Documentation cache and generated files:
21
+ /.yardoc/
22
+ /_yardoc/
23
+ /doc/
24
+ /rdoc/
25
+
26
+ ## Environment normalisation:
27
+ /.bundle/
28
+ /vendor/bundle
29
+ /lib/bundler/man/
30
+
31
+ # for a library or gem, you might want to ignore these files since the code is
32
+ # intended to run in multiple environments; otherwise, check them in:
33
+ # Gemfile.lock
34
+ # .ruby-version
35
+ # .ruby-gemset
36
+
37
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
38
+ .rvmrc
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-2.2.2
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,36 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ figtree (0.0.1)
5
+ parslet
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ blankslate (3.1.3)
11
+ diff-lcs (1.2.5)
12
+ parslet (1.7.1)
13
+ blankslate (>= 2.0, <= 4.0)
14
+ rspec (3.3.0)
15
+ rspec-core (~> 3.3.0)
16
+ rspec-expectations (~> 3.3.0)
17
+ rspec-mocks (~> 3.3.0)
18
+ rspec-core (3.3.2)
19
+ rspec-support (~> 3.3.0)
20
+ rspec-expectations (3.3.1)
21
+ diff-lcs (>= 1.2.0, < 2.0)
22
+ rspec-support (~> 3.3.0)
23
+ rspec-mocks (3.3.2)
24
+ diff-lcs (>= 1.2.0, < 2.0)
25
+ rspec-support (~> 3.3.0)
26
+ rspec-support (3.3.0)
27
+
28
+ PLATFORMS
29
+ ruby
30
+
31
+ DEPENDENCIES
32
+ figtree!
33
+ rspec (>= 3.0.0)
34
+
35
+ BUNDLED WITH
36
+ 1.10.6
data/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # Figtree
2
+ ## about
3
+ 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. :)
4
+
5
+ ## performance
6
+ A typical `.ini` file takes slightly less than 0.02s to be parsed, transformed, and loaded.
7
+
8
+ ## installation
9
+ `gem install figtree`
10
+
11
+ ## usage
12
+ require 'figtree'
13
+ config = Figtree.load_config('spec/support/settings.conf')
14
+ config.common.basic_size_limit
15
+ => 26214400
16
+
17
+ # development
18
+ ## installation
19
+ `bundle install`
20
+
21
+ ## tests
22
+ `rspec spec/`
23
+
24
+ ### TODO
25
+ - refactor marked TODO listings in files (mostly refactoring to generic)
26
+ - allow indifferent access? (not just dot notation but allow hash access)
27
+ - add more unit test coverage to Transformer
28
+ - seems like Parslet doesn't have a `TransformFailed` error format, worth adding one?
data/figtree.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+ require "figtree/version"
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.name = 'figtree'
6
+ gem.version = Figtree::VERSION
7
+ gem.date = Date.today.to_s
8
+
9
+ gem.summary = "A parser and transformer for loading `.ini` files into Ruby dot notation accessible objects."
10
+ gem.description = "See README.md"
11
+
12
+ gem.authors = ['Alex Moore-Niemi']
13
+ gem.email = 'moore.niemi@gmail.com'
14
+ gem.homepage = 'https://github.com/mooreniemi/figtree'
15
+
16
+ gem.add_runtime_dependency 'parslet', '~> 1.7'
17
+ gem.add_development_dependency 'rspec', '~> 3.0', '>= 3.0.0'
18
+
19
+ gem.files = `git ls-files`.split("\n")
20
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
21
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
22
+ gem.require_paths = ["lib"]
23
+ end
data/lib/figtree.rb ADDED
@@ -0,0 +1,30 @@
1
+ require 'figtree/parser'
2
+ require 'figtree/transformer'
3
+ require 'figtree/ini_config'
4
+
5
+ module Figtree
6
+ def self.load_config(file_path, overrides=[])
7
+ IniConfig.new(
8
+ figgy_transform(
9
+ figgy_parse(
10
+ File.read(file_path)
11
+ ),
12
+ overrides
13
+ )
14
+ )
15
+ end
16
+
17
+ private
18
+ def self.figgy_parse(str)
19
+ Parser.new.parse(str)
20
+ rescue Parslet::ParseFailed => failure
21
+ puts failure.cause.ascii_tree
22
+ end
23
+
24
+ def self.figgy_transform(tree, overrides = [])
25
+ Transformer.new(overrides).apply(tree)
26
+ rescue
27
+ puts 'failed transform'
28
+ {} # returning hash just to preserve stack trace
29
+ end
30
+ end
@@ -0,0 +1,10 @@
1
+ require 'ostruct'
2
+ module Figtree
3
+ class IniConfig < OpenStruct
4
+ def initialize(array = [])
5
+ # TODO move the reduction to #load_config
6
+ # TODO then this class can be a shell
7
+ super(array.reduce({}, :merge))
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,91 @@
1
+ require 'parslet'
2
+
3
+ module Figtree
4
+ # ConFIG into a Tree :)
5
+ class Parser < Parslet::Parser
6
+ rule(:eof) { any.absent? }
7
+ 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) }
10
+ rule(:grouper) do
11
+ str('[') >>
12
+ group_title.as(:group_title) >>
13
+ str(']')
14
+ end
15
+
16
+ rule(:comment) do
17
+ str(';') >>
18
+ (newline.absent? >> any).repeat
19
+ end
20
+
21
+ rule(:string) do
22
+ str('"') >>
23
+ ((str('\\') >> any) | (str('"').absent? >> any)).repeat.as(:string) >>
24
+ str('"')
25
+ end
26
+
27
+ rule(:boolean) do
28
+ (str('no') | str('yes')).as(:boolean)
29
+ end
30
+
31
+ rule(:number) do
32
+ match('[0-9]').repeat(1).as(:number)
33
+ end
34
+
35
+ rule(:array) do
36
+ (match('[a-zA-Z]').repeat(1) >>
37
+ (str(',') >>
38
+ match('[a-zA-Z]').repeat(1)).repeat.maybe).maybe.as(:array) >>
39
+ (str(',') | newline | eof)
40
+ end
41
+
42
+ rule(:file_path) do
43
+ match('[/a-z/]').repeat(1).as(:file_path)
44
+ end
45
+
46
+ rule(:snake_case_key) do
47
+ match('[a-zA-Z0-9_]').repeat(1).as(:snake_case_key)
48
+ end
49
+
50
+ rule(:snakey_option_key) do
51
+ snake_case_key.as(:key_to_be_overridden) >>
52
+ str('<') >>
53
+ snake_case_key.as(:optional_key) >>
54
+ str('>')
55
+ end
56
+
57
+ rule(:assignment) do
58
+ snake_case_key >>
59
+ space >>
60
+ str("=") >>
61
+ space >>
62
+ (number | boolean | array | snake_case_key | file_path | string)
63
+ end
64
+
65
+ rule(:override_assignment) do
66
+ snakey_option_key >>
67
+ space >>
68
+ str("=") >>
69
+ space >>
70
+ file_path
71
+ end
72
+
73
+ rule(:assignment_or_comment) do
74
+ ( comment | assignment | override_assignment )
75
+ end
76
+
77
+ rule(:group_member) do
78
+ newline.maybe >>
79
+ assignment_or_comment >>
80
+ newline.maybe
81
+ end
82
+
83
+ rule(:group) do
84
+ (grouper >>
85
+ group_member.repeat.maybe).as(:group).
86
+ repeat.maybe
87
+ end
88
+
89
+ root(:group)
90
+ end
91
+ end
@@ -0,0 +1,64 @@
1
+ require 'parslet'
2
+ require 'ostruct'
3
+
4
+ module Figtree
5
+ # once you have an AST, you can do type transformations
6
+ class Transformer < Parslet::Transform
7
+ attr_accessor :overrides
8
+
9
+ def initialize(overrides = [], &block)
10
+ @overrides = overrides
11
+ super(&block)
12
+ end
13
+
14
+ # TODO these could largely be consolidated with some rearrangement
15
+ # TODO subtree is considered too flexible, switch to simple(:x)?
16
+ rule(:snake_case_key => simple(:key), :number => subtree(:value)) do
17
+ {
18
+ key.to_sym => Integer(value)
19
+ }
20
+ end
21
+ rule(:snake_case_key => simple(:key), :array => subtree(:value)) do
22
+ {
23
+ key.to_sym => Array(value)
24
+ }
25
+ end
26
+ rule(:snake_case_key => simple(:key), :string => subtree(:value)) do
27
+ {
28
+ key.to_sym => String(value)
29
+ }
30
+ end
31
+ rule(:snake_case_key => simple(:key), :boolean => simple(:value)) do
32
+ {
33
+ # Boolean(value) fails
34
+ key.to_sym => String(value)
35
+ }
36
+ end
37
+ rule(:snake_case_key => simple(:key), :file_path => subtree(:value)) do
38
+ {
39
+ key.to_sym => String(value)
40
+ }
41
+ end
42
+ rule(
43
+ :key_to_be_overridden => subtree(:a),
44
+ :optional_key => subtree(:b),
45
+ :file_path => subtree(:c)
46
+ ) do
47
+ if !@overrides.nil? && @overrides.include?(b[:snake_case_key])
48
+ {
49
+ a[:snake_case_key] => String(c)
50
+ }
51
+ else
52
+ {
53
+ }
54
+ end
55
+ end
56
+ rule(:group => subtree(:group_members)) do
57
+ group_title = group_members[0][:group_title].to_sym
58
+ group_values = OpenStruct.new(group_members[1..-1].reduce({}, :merge))
59
+ {
60
+ group_title => group_values
61
+ }
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,3 @@
1
+ module Figtree
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+
3
+ module Figtree
4
+ describe Parser do
5
+ let(:parser) { Parser.new }
6
+ it 'can parse newlines' do
7
+ expect(parser.newline).to parse("\n")
8
+ end
9
+ it 'can parse group names' do
10
+ expect(parser.grouper).to parse('[common]')
11
+ end
12
+ it 'can parse comments' do
13
+ expect(parser.comment).to parse('; This is a comment\n')
14
+ end
15
+ it 'can parse snake_case keys' do
16
+ expect(parser.snake_case_key).to parse('basic_size_limit')
17
+ end
18
+ it 'can parse strings' do
19
+ expect(parser.string).to parse('"hello there, ftp uploading"')
20
+ end
21
+ it 'can parse arrays' do
22
+ expect(parser.array).to parse("a,")
23
+ expect(parser.array).to parse("a,b")
24
+ expect(parser.array).to parse("a,b,c\n")
25
+ expect(parser.array).to_not parse(',,')
26
+ expect(parser.array).to parse("array,of,values\n")
27
+ expect(parser.array).to parse("array,of,values")
28
+ end
29
+ it 'can parse numbers' do
30
+ expect(parser.number).to parse("26214400")
31
+ end
32
+ it 'can parse booleans flexibly' do
33
+ expect(parser.boolean).to parse("no")
34
+ expect(parser.boolean).to parse("yes")
35
+ end
36
+ it 'can parse assignments' do
37
+ expect(parser.assignment).to parse('basic_size_limit = 26214400')
38
+ expect(parser.assignment).to parse('path = /srv/var/tmp/')
39
+ expect(parser.assignment).to_not parse('path<itscript> = /srv/tmp/')
40
+ expect(parser.assignment).to parse('name = "hello there, ftp uploading"')
41
+ expect(parser.assignment).to parse('params = array,of,values')
42
+ end
43
+ it 'can parse keys with optional overrides' do
44
+ expect(parser.snakey_option_key).to parse('path<itscript>')
45
+ end
46
+ it 'can parse file_paths' do
47
+ expect(parser.file_path).to parse('/srv/tmp/')
48
+ end
49
+ it 'can parse overrides' do
50
+ expect(parser.override_assignment).to parse('path<itscript> = /srv/tmp/')
51
+ end
52
+
53
+ describe "using the settings.conf file for input" do
54
+ let(:settings_path) { 'spec/support/settings.conf' }
55
+ let(:multi_group) {
56
+ [
57
+ "[common]",
58
+ "basic_size_limit = 234234",
59
+ "[rare]",
60
+ "pepes = 0"
61
+ ].join("\n") + "\n"
62
+ }
63
+
64
+ it 'parses a group member' do
65
+ expect(parser.group_member).to parse("\nbasic_size_limit = 26214400\n")
66
+ end
67
+ it 'can parse single assignment inside a group' do
68
+ expect(parser.group).
69
+ to parse("[common]\nbasic_size_limit = 26214400\n")
70
+ end
71
+ it 'can parse multiple assignments inside a group' do
72
+ expect(parser.group).
73
+ to parse("[common]\nbasic_size_limit = 26214400\nstudent_size_limit = 52428800\n")
74
+ end
75
+ it 'can parse multiple groups' do
76
+ expect(parser.group).to parse(multi_group)
77
+ end
78
+
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ module Figtree
4
+ describe Transformer do
5
+ let(:tree) do
6
+ Parser.new.parse("[common]\nbasic_size_limit = 26214400\n")
7
+ end
8
+ it 'can apply an int type conversion' do
9
+ expect(Transformer.new.apply(tree)).to eq(
10
+ [
11
+ {
12
+ common: OpenStruct.new({basic_size_limit: 26214400})
13
+ }
14
+ ]
15
+ )
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+ # for the performance test at bottom
3
+ require 'benchmark'
4
+
5
+ describe Figtree do
6
+ describe '#load_config' do
7
+ let(:settings_path) { 'spec/support/settings.conf' }
8
+ let(:common) do
9
+ OpenStruct.new(
10
+ :basic_size_limit => 26214400,
11
+ :student_size_limit => 52428800,
12
+ :paid_users_size_limit => 2147483648,
13
+ :path => "/srv/var/tmp/",
14
+ )
15
+ end
16
+
17
+ let(:common_with_override) do
18
+ OpenStruct.new(
19
+ :basic_size_limit => 26214400,
20
+ :student_size_limit => 52428800,
21
+ :paid_users_size_limit => 2147483648,
22
+ :path => "/srv/var/tmp/",
23
+ )
24
+ end
25
+
26
+ let(:the_whole_kebab) do
27
+ Figtree::IniConfig.new(
28
+ [
29
+ {
30
+ common: OpenStruct.new(
31
+ {
32
+ :basic_size_limit => 26214400,
33
+ :student_size_limit=> 52428800,
34
+ :paid_users_size_limit=> 2147483648,
35
+ :path=> "/srv/var/tmp/"
36
+ }
37
+ )
38
+ },
39
+ {
40
+ ftp: OpenStruct.new(
41
+ {
42
+ :name => "hello there, ftp uploading",
43
+ :path => "/tmp/",
44
+ :enabled => "no"
45
+ }
46
+ )
47
+ },
48
+ {
49
+ http: OpenStruct.new(
50
+ {
51
+ :name => "http uploading",
52
+ :path => "/tmp/",
53
+ :params => ["array,of,values"]
54
+ }
55
+ )
56
+ }
57
+ ]
58
+ )
59
+ end
60
+
61
+ it 'can parse a group and provide dot notation access' do
62
+ expect(Figtree.load_config(settings_path).common).to eq(common)
63
+ end
64
+ it 'can parse the overrides correctly' do
65
+ expect(Figtree.load_config(settings_path, [:itscript]).common).
66
+ to eq(common_with_override)
67
+ end
68
+ it 'can parse the whole Kebab without any misunderstandings' do
69
+ expect(Figtree.load_config(settings_path)).to eq(the_whole_kebab)
70
+ end
71
+
72
+ it 'can parse the whole ini file quickly' do
73
+ expect(
74
+ Benchmark.realtime do
75
+ Figtree.load_config(settings_path)
76
+ end
77
+ ).to be < 0.02
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,13 @@
1
+ require 'bundler/setup'
2
+ Bundler.setup
3
+
4
+ require 'ostruct'
5
+ require 'parslet'
6
+ require 'parslet/rig/rspec'
7
+
8
+ Dir[File.join(File.dirname(__FILE__), "..", "lib" , "**.rb")].each do |file|
9
+ require file
10
+ end
11
+
12
+ RSpec.configure do |config|
13
+ end
@@ -0,0 +1,21 @@
1
+ [common]
2
+ basic_size_limit = 26214400
3
+ student_size_limit = 52428800
4
+ paid_users_size_limit = 2147483648
5
+ path = /srv/var/tmp/
6
+ path<itscript> = /srv/tmp/
7
+ [ftp]
8
+ name = "hello there, ftp uploading"
9
+ path = /tmp/
10
+ path<production> = /srv/var/tmp/
11
+ path<staging> = /srv/uploads/
12
+ path<ubuntu> = /etc/var/uploads
13
+ enabled = no
14
+ ; This is a comment
15
+ [http]
16
+ name = "http uploading"
17
+ path = /tmp/
18
+ path<production> = /srv/var/tmp/
19
+ path<staging> = /srv/uploads/; This is another comment
20
+ params = array,of,values
21
+
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: figtree
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Alex Moore-Niemi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-10-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: parslet
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.0'
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 3.0.0
37
+ type: :development
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '3.0'
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 3.0.0
47
+ description: See README.md
48
+ email: moore.niemi@gmail.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - ".gitignore"
54
+ - ".ruby-version"
55
+ - Gemfile
56
+ - Gemfile.lock
57
+ - README.md
58
+ - figtree.gemspec
59
+ - lib/figtree.rb
60
+ - lib/figtree/ini_config.rb
61
+ - lib/figtree/parser.rb
62
+ - lib/figtree/transformer.rb
63
+ - lib/figtree/version.rb
64
+ - spec/figtree/parser_spec.rb
65
+ - spec/figtree/transformer_spec.rb
66
+ - spec/figtree_spec.rb
67
+ - spec/spec_helper.rb
68
+ - spec/support/settings.conf
69
+ homepage: https://github.com/mooreniemi/figtree
70
+ licenses: []
71
+ metadata: {}
72
+ post_install_message:
73
+ rdoc_options: []
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ requirements: []
87
+ rubyforge_project:
88
+ rubygems_version: 2.4.6
89
+ signing_key:
90
+ specification_version: 4
91
+ summary: A parser and transformer for loading `.ini` files into Ruby dot notation
92
+ accessible objects.
93
+ test_files:
94
+ - spec/figtree/parser_spec.rb
95
+ - spec/figtree/transformer_spec.rb
96
+ - spec/figtree_spec.rb
97
+ - spec/spec_helper.rb
98
+ - spec/support/settings.conf
99
+ has_rdoc: