config_volumizer 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 51fccd17634bc4c6605169c3f0aaa1552ed7e09b
4
+ data.tar.gz: 7eedb6855853b5e3e643bfd95dc4286b4ef6a654
5
+ SHA512:
6
+ metadata.gz: a451d91280f236cbc6fc3442f1be856542d5042f7ba1113fb4996a78f11b44a4f121c342edcb76138eea9459dffb553c71638b65ad7bf5b64acbfd56285fce6c
7
+ data.tar.gz: 493db1a62e270c7f0d01c4ca2545b85f9e38c33760ae6b3f4b24466210beff9dfda2625939bad0eed7dbdb058a869df008247017d97de59496058b326de97109
data/.document ADDED
@@ -0,0 +1,3 @@
1
+ -
2
+ ChangeLog.md
3
+ LICENSE.txt
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ .ruby-version
2
+ Gemfile.lock
3
+ doc/
4
+ pkg/
5
+ vendor/cache/*.gem
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour --format documentation
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - "2.0.0"
4
+ - "2.1.5"
5
+ - "2.2.1"
6
+ script: "bundle exec rspec"
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --markup markdown --title "config_volumizer Documentation" --protected
data/ChangeLog.md ADDED
@@ -0,0 +1,4 @@
1
+ ### 0.1.0 / 2015-04-03
2
+
3
+ * Initial release:
4
+
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ gem 'kramdown'
7
+ gem 'bundler'
8
+ gem 'rake'
9
+ gem 'rspec'
10
+ gem 'rubygems-tasks'
11
+ gem 'yard'
12
+ gem 'flay'
13
+ gem 'flog'
14
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2015 PayrollHero Pte. Ltd.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,65 @@
1
+ # config_volumizer
2
+
3
+ [![Build Status](https://travis-ci.org/payrollhero/config_volumizer.svg)](https://travis-ci.org/payrollhero/config_volumizer)
4
+ [![Code Climate](https://codeclimate.com/github/payrollhero/config_volumizer/badges/gpa.svg)](https://codeclimate.com/github/payrollhero/config_volumizer)
5
+
6
+ * [Homepage](https://rubygems.org/gems/config_volumizer)
7
+ * [Documentation](http://rubydoc.info/gems/config_volumizer/frames)
8
+ * [Email](mailto:piotr.banasik at gmail.com)
9
+
10
+ ## Description
11
+
12
+ Ever wanted to move to ENV based 12 Factor config but are stuck with somewhat complex yaml (or simillar) files
13
+ holding all your settings?
14
+
15
+ Been scratching your head how to convert your deeply nested config to ENV?
16
+
17
+ Config Volumizer comes to the rescue.
18
+
19
+ ## Examples
20
+
21
+ Convert this:
22
+ ```yaml
23
+ some:
24
+ setting:
25
+ - one
26
+ - two
27
+ - three
28
+ with:
29
+ another: setting
30
+ ```
31
+
32
+ info a series of ENV variables like so:
33
+
34
+ ```
35
+ some.setting[0] = one
36
+ some.setting[1] = two
37
+ some.setting[2] = three
38
+ some.with.another = setting
39
+ ```
40
+
41
+ ... and then just give it some volume with the volumizer to turn it back to the original rich structure
42
+
43
+ ```ruby
44
+ ConfigVolumizer.parse(ENV, 'some')
45
+ ```
46
+
47
+ ## Features
48
+
49
+ The gem basically smartly parses all keys within the passed in Hash that match the prefix specified.
50
+
51
+ It allows to nest either arrays or hashes inside eachother, basically any reasonable combination of hash/array notation should work.
52
+
53
+ ## Install
54
+
55
+ Add it to your gemfile and use it.
56
+
57
+ ```ruby
58
+ gem 'config_volumizer'
59
+ ```
60
+
61
+ ## Copyright
62
+
63
+ Copyright (c) 2015 PayrollHero Pte. Ltd.
64
+
65
+ See LICENSE.txt for details.
data/Rakefile ADDED
@@ -0,0 +1,34 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+
5
+ begin
6
+ require 'bundler'
7
+ rescue LoadError => e
8
+ warn e.message
9
+ warn "Run `gem install bundler` to install Bundler."
10
+ exit -1
11
+ end
12
+
13
+ begin
14
+ Bundler.setup(:development)
15
+ rescue Bundler::BundlerError => e
16
+ warn e.message
17
+ warn "Run `bundle install` to install missing gems."
18
+ exit e.status_code
19
+ end
20
+
21
+ require 'rake'
22
+
23
+ require 'rubygems/tasks'
24
+ Gem::Tasks.new
25
+
26
+ require 'rspec/core/rake_task'
27
+ RSpec::Core::RakeTask.new
28
+
29
+ task :test => :spec
30
+ task :default => :spec
31
+
32
+ require 'yard'
33
+ YARD::Rake::YardocTask.new
34
+ task :doc => :yard
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require File.expand_path('../lib/config_volumizer/version', __FILE__)
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = "config_volumizer"
7
+ gem.version = ConfigVolumizer::VERSION
8
+ gem.summary = "Adds volume to an otherwise flat config"
9
+ gem.description = "Allows turning dot notation config from ENV to rich Hash/Array structures"
10
+ gem.license = "MIT"
11
+ gem.authors = ["Piotr Banasik"]
12
+ gem.email = "piotr.banasik@gmail.com"
13
+ gem.homepage = "https://rubygems.org/gems/config_volumizer"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ['lib']
19
+ end
@@ -0,0 +1,82 @@
1
+ require 'strscan'
2
+ require 'yaml'
3
+
4
+ module ConfigVolumizer
5
+ module Parser
6
+
7
+ class << self
8
+
9
+ def parse(source, base_name)
10
+ result = {}
11
+ source.each do |key, value|
12
+ if matches_name(base_name, key)
13
+ fragments = key.gsub(/^#{base_name}/, '')
14
+ handle_item(result, base_name, fragments, value)
15
+ end
16
+ end
17
+ result
18
+ end
19
+
20
+ private
21
+
22
+ def matches_name(base_name, key)
23
+ key == base_name || key =~ /^#{base_name}([.\[])/
24
+ end
25
+
26
+ def format_value(value)
27
+ YAML.load(value)
28
+ end
29
+
30
+ def initialize_array(result, key)
31
+ validate_result_key_kind(result, key, Array)
32
+ result[key] ||= []
33
+ end
34
+
35
+ def initialize_hash(result, key)
36
+ validate_result_key_kind(result, key, Hash)
37
+ result[key] ||= {}
38
+ end
39
+
40
+ def validate_result_key_kind(result, key, kind)
41
+ if result[key] && !result[key].kind_of?(kind)
42
+ raise ArgumentError, "referencing #{kind} key #{name} when its already a #{result[key].class}"
43
+ end
44
+ end
45
+
46
+ def handle_item(result, base_name, name, value)
47
+ dst, key = result, base_name
48
+ scanner = StringScanner.new(name)
49
+
50
+ until scanner.eos?
51
+ dst, key = case next_fragment(scanner)
52
+ when /^\.(.+)/
53
+ handle_hash($~, dst, key)
54
+ when /\[(\d+)\]/
55
+ handle_array($~, dst, key)
56
+ end
57
+ end
58
+
59
+ dst[key] = format_value(value)
60
+ end
61
+
62
+ def handle_array(match, dst, key)
63
+ index = match[1].to_i
64
+ initialize_array(dst, key)
65
+ return dst[key], index
66
+ end
67
+
68
+ def handle_hash(match, dst, key)
69
+ hash_key = match[1]
70
+ initialize_hash(dst, key)
71
+ return dst[key], hash_key
72
+ end
73
+
74
+ def next_fragment(scanner)
75
+ fragment = scanner.scan /\.[^.\[]+|\[\d+\]/
76
+ raise "failed: rest: #{scanner.rest} inside #{scanner.string}" unless fragment
77
+ fragment
78
+ end
79
+
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,4 @@
1
+ module ConfigVolumizer
2
+ # config_volumizer version
3
+ VERSION = "0.1.0"
4
+ end
@@ -0,0 +1,12 @@
1
+ require 'config_volumizer/version'
2
+ require 'config_volumizer/parser'
3
+
4
+ module ConfigVolumizer
5
+ class << self
6
+
7
+ def parse(source, base_name)
8
+ Parser.parse(source, base_name)
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,224 @@
1
+ require 'spec_helper'
2
+
3
+ describe ConfigVolumizer do
4
+ let(:key) { 'ex' }
5
+ let(:result) { described_class.parse(input, key) }
6
+
7
+ describe "1 level hash" do
8
+ let(:input) { { "ex" => "1a" } }
9
+ let(:expected_result) { { 'ex' => '1a' } }
10
+
11
+ example do
12
+ expect(result).to eq(expected_result)
13
+ end
14
+ end
15
+
16
+ describe "2 level hash" do
17
+ let(:key) { 'ex1' }
18
+ let(:input) do
19
+ {
20
+ "ex1.bar" => "1a",
21
+ "ex1.bar2" => "2a",
22
+ }
23
+ end
24
+ let(:expected_result) do
25
+ {
26
+ 'ex1' => {
27
+ 'bar' => '1a',
28
+ 'bar2' => '2a',
29
+ },
30
+ }
31
+ end
32
+
33
+ example do
34
+ expect(result).to eq(expected_result)
35
+ end
36
+ end
37
+
38
+ describe "3 level hash" do
39
+ let(:key) { 'ex3' }
40
+ let(:input) do
41
+ {
42
+ "ex3.one.two" => "1a",
43
+ "ex3.one.three" => "2a",
44
+ "ex3.two.three" => "3a",
45
+ }
46
+ end
47
+ let(:expected_result) do
48
+ {
49
+ 'ex3' => {
50
+ 'one' => {
51
+ 'two' => '1a',
52
+ 'three' => '2a',
53
+ },
54
+ 'two' => {
55
+ 'three' => '3a',
56
+ },
57
+ },
58
+ }
59
+ end
60
+
61
+ example do
62
+ expect(result).to eq(expected_result)
63
+ end
64
+ end
65
+
66
+ describe "array of values in hash" do
67
+ let(:input) do
68
+ {
69
+ "ex.one[0]" => "1a",
70
+ "ex.one[1]" => "2a",
71
+ }
72
+ end
73
+ let(:expected_result) do
74
+ {
75
+ 'ex' => { 'one' => ['1a', '2a'] }
76
+ }
77
+ end
78
+
79
+ example do
80
+ expect(result).to eq(expected_result)
81
+ end
82
+ end
83
+
84
+ describe "just array of values" do
85
+ let(:input) do
86
+ {
87
+ "ex[0]" => "1a",
88
+ "ex[1]" => "2a",
89
+ }
90
+ end
91
+ let(:expected_result) do
92
+ {
93
+ 'ex' => ['1a', '2a']
94
+ }
95
+ end
96
+
97
+ example do
98
+ expect(result).to eq(expected_result)
99
+ end
100
+ end
101
+
102
+ describe "array of hashes" do
103
+ let(:input) do
104
+ {
105
+ "ex[0].foo" => "1a",
106
+ "ex[0].bar" => "2a",
107
+ "ex[1].foo" => "3a",
108
+ }
109
+ end
110
+ let(:expected_result) do
111
+ {
112
+ 'ex' => [
113
+ { 'foo' => '1a', 'bar' => '2a' },
114
+ { 'foo' => '3a' },
115
+ ]
116
+ }
117
+ end
118
+
119
+ example do
120
+ expect(result).to eq(expected_result)
121
+ end
122
+ end
123
+
124
+ describe "array of hash in hash" do
125
+ let(:key) { 'ex2' }
126
+ let(:input) do
127
+ {
128
+ "ex2.one[0].foo" => "1a",
129
+ "ex2.one[0].bar" => "2a",
130
+ "ex2.one[1].foo" => "3a",
131
+ }
132
+ end
133
+ let(:expected_result) do
134
+ {
135
+ 'ex2' => {
136
+ 'one' => [
137
+ {
138
+ "foo" => '1a',
139
+ "bar" => '2a',
140
+ },
141
+ {
142
+ "foo" => '3a',
143
+ }
144
+ ],
145
+ },
146
+ }
147
+ end
148
+
149
+ example do
150
+ expect(result).to eq(expected_result)
151
+ end
152
+ end
153
+
154
+ describe "empty hash value" do
155
+ let(:input) { { "ex" => "{}", } }
156
+ let(:expected_result) { { 'ex' => {} } }
157
+
158
+ example do
159
+ expect(result).to eq(expected_result)
160
+ end
161
+ end
162
+
163
+ describe "empty array value" do
164
+ let(:input) { { "ex" => "[]", } }
165
+ let(:expected_result) { { 'ex' => [] } }
166
+
167
+ example do
168
+ expect(result).to eq(expected_result)
169
+ end
170
+ end
171
+
172
+ describe "2d arrays" do
173
+ let(:input) do
174
+ {
175
+ "ex[0][0]" => "1a",
176
+ "ex[0][1]" => "2a",
177
+ "ex[1][0]" => "3a",
178
+ "ex[1][1]" => "4a",
179
+ }
180
+ end
181
+ let(:expected_result) do
182
+ {
183
+ 'ex' => [
184
+ ['1a', '2a'],
185
+ ['3a', '4a']
186
+ ]
187
+ }
188
+ end
189
+
190
+ example do
191
+ expect(result).to eq(expected_result)
192
+ end
193
+ end
194
+
195
+ describe "3d arrays" do
196
+ let(:input) do
197
+ {
198
+ 'ex[0][0][1]' => "1a",
199
+ 'ex[0][1][0]' => "2a",
200
+ 'ex[1][0][1]' => "3a",
201
+ 'ex[1][1][0]' => "4a",
202
+ }
203
+ end
204
+ let(:expected_result) do
205
+ {
206
+ 'ex' => [
207
+ [
208
+ [nil, '1a'],
209
+ ['2a'],
210
+ ],
211
+ [
212
+ [nil, '3a'],
213
+ ['4a'],
214
+ ]
215
+ ]
216
+ }
217
+ end
218
+
219
+ example do
220
+ expect(result).to eq(expected_result)
221
+ end
222
+ end
223
+
224
+ end
@@ -0,0 +1,2 @@
1
+ require 'rspec'
2
+ require 'config_volumizer'
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: config_volumizer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Piotr Banasik
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-04-03 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Allows turning dot notation config from ENV to rich Hash/Array structures
14
+ email: piotr.banasik@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - ".document"
20
+ - ".gitignore"
21
+ - ".rspec"
22
+ - ".travis.yml"
23
+ - ".yardopts"
24
+ - ChangeLog.md
25
+ - Gemfile
26
+ - LICENSE.txt
27
+ - README.md
28
+ - Rakefile
29
+ - config_volumizer.gemspec
30
+ - lib/config_volumizer.rb
31
+ - lib/config_volumizer/parser.rb
32
+ - lib/config_volumizer/version.rb
33
+ - spec/config_volumizer_spec.rb
34
+ - spec/spec_helper.rb
35
+ homepage: https://rubygems.org/gems/config_volumizer
36
+ licenses:
37
+ - MIT
38
+ metadata: {}
39
+ post_install_message:
40
+ rdoc_options: []
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ requirements: []
54
+ rubyforge_project:
55
+ rubygems_version: 2.4.5
56
+ signing_key:
57
+ specification_version: 4
58
+ summary: Adds volume to an otherwise flat config
59
+ test_files:
60
+ - spec/config_volumizer_spec.rb
61
+ - spec/spec_helper.rb