figtree 0.0.1

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 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: