levels 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.
Files changed (90) hide show
  1. data/.gitignore +17 -0
  2. data/.rbenv-version +1 -0
  3. data/CHANGELOG.md +4 -0
  4. data/Gemfile +12 -0
  5. data/Guardfile +14 -0
  6. data/LICENSE +22 -0
  7. data/README.md +315 -0
  8. data/Rakefile +28 -0
  9. data/bin/levels +130 -0
  10. data/examples/01_base.rb +6 -0
  11. data/examples/01_merge_to_json.sh +27 -0
  12. data/examples/01_prod.json +8 -0
  13. data/examples/02_base.rb +4 -0
  14. data/examples/02_merge_with_file.sh +20 -0
  15. data/examples/02_value +1 -0
  16. data/levels.gemspec +20 -0
  17. data/lib/levels.rb +77 -0
  18. data/lib/levels/audit.rb +24 -0
  19. data/lib/levels/audit/group_observer.rb +26 -0
  20. data/lib/levels/audit/nested_group_observer.rb +37 -0
  21. data/lib/levels/audit/root_observer.rb +63 -0
  22. data/lib/levels/audit/value.rb +64 -0
  23. data/lib/levels/audit/value_observer.rb +46 -0
  24. data/lib/levels/audit/values.rb +66 -0
  25. data/lib/levels/configuration.rb +98 -0
  26. data/lib/levels/configured_group.rb +62 -0
  27. data/lib/levels/event_handler.rb +127 -0
  28. data/lib/levels/group.rb +61 -0
  29. data/lib/levels/input/json.rb +17 -0
  30. data/lib/levels/input/ruby.rb +120 -0
  31. data/lib/levels/input/system.rb +63 -0
  32. data/lib/levels/input/yaml.rb +17 -0
  33. data/lib/levels/key.rb +28 -0
  34. data/lib/levels/key_values.rb +54 -0
  35. data/lib/levels/lazy_evaluator.rb +54 -0
  36. data/lib/levels/level.rb +80 -0
  37. data/lib/levels/method_missing.rb +14 -0
  38. data/lib/levels/output/json.rb +33 -0
  39. data/lib/levels/output/system.rb +29 -0
  40. data/lib/levels/output/yaml.rb +19 -0
  41. data/lib/levels/runtime.rb +30 -0
  42. data/lib/levels/setup.rb +132 -0
  43. data/lib/levels/system/constants.rb +8 -0
  44. data/lib/levels/system/key_formatter.rb +15 -0
  45. data/lib/levels/system/key_generator.rb +50 -0
  46. data/lib/levels/system/key_parser.rb +67 -0
  47. data/lib/levels/version.rb +3 -0
  48. data/test/acceptance/audit_test.rb +105 -0
  49. data/test/acceptance/event_handler_test.rb +43 -0
  50. data/test/acceptance/read_json_test.rb +35 -0
  51. data/test/acceptance/read_ruby_test.rb +117 -0
  52. data/test/acceptance/read_system_test.rb +121 -0
  53. data/test/acceptance/read_yaml_test.rb +38 -0
  54. data/test/acceptance/setup_test.rb +115 -0
  55. data/test/acceptance/write_json_test.rb +39 -0
  56. data/test/acceptance/write_system_test.rb +68 -0
  57. data/test/acceptance/write_yaml_test.rb +33 -0
  58. data/test/bin/merge_test.rb +194 -0
  59. data/test/bin/options_test.rb +41 -0
  60. data/test/helper.rb +12 -0
  61. data/test/support/acceptance_spec.rb +58 -0
  62. data/test/support/base_spec.rb +14 -0
  63. data/test/support/bin_spec.rb +65 -0
  64. data/test/support/tempfile_helper.rb +35 -0
  65. data/test/unit/audit/group_observer_test.rb +24 -0
  66. data/test/unit/audit/nested_group_observer_test.rb +28 -0
  67. data/test/unit/audit/root_observer_test.rb +54 -0
  68. data/test/unit/audit/value_observer_test.rb +63 -0
  69. data/test/unit/audit/value_test.rb +41 -0
  70. data/test/unit/audit/values_test.rb +86 -0
  71. data/test/unit/configuration_test.rb +72 -0
  72. data/test/unit/configured_group_test.rb +75 -0
  73. data/test/unit/group_test.rb +105 -0
  74. data/test/unit/input/json_test.rb +32 -0
  75. data/test/unit/input/ruby_test.rb +140 -0
  76. data/test/unit/input/system_test.rb +59 -0
  77. data/test/unit/input/yaml_test.rb +33 -0
  78. data/test/unit/key_test.rb +45 -0
  79. data/test/unit/key_values_test.rb +106 -0
  80. data/test/unit/lazy_evaluator_test.rb +38 -0
  81. data/test/unit/level_test.rb +89 -0
  82. data/test/unit/levels_test.rb +23 -0
  83. data/test/unit/output/json_test.rb +55 -0
  84. data/test/unit/output/system_test.rb +32 -0
  85. data/test/unit/output/yaml_test.rb +38 -0
  86. data/test/unit/runtime_test.rb +40 -0
  87. data/test/unit/system/key_formatter_test.rb +43 -0
  88. data/test/unit/system/key_generator_test.rb +21 -0
  89. data/test/unit/system/key_parser_test.rb +207 -0
  90. metadata +215 -0
@@ -0,0 +1,59 @@
1
+ require 'helper'
2
+
3
+ describe Levels::Input::System do
4
+
5
+ let(:template) { {} }
6
+ let(:key_formatter) { nil }
7
+ let(:env_hash) { {} }
8
+
9
+ subject { Levels::Input::System.new(template.to_enum, key_formatter, env_hash) }
10
+
11
+ [nil, "MY_"].each do |prefix|
12
+ describe "finding data in the System with #{prefix || 'no'} prefix" do
13
+
14
+ let(:key_formatter) { Levels::System::KeyFormatter.new(prefix) }
15
+
16
+ before do
17
+ template[:sample] = { hello: "world" }.to_enum
18
+ template[:settings] = { hello: "world" }.to_enum
19
+ end
20
+
21
+ it "finds variables that exist in the template" do
22
+ env_hash["#{prefix}SAMPLE_HELLO"] = "universe"
23
+
24
+ assert_input_equals_hash(
25
+ sample: {
26
+ hello: "universe"
27
+ }
28
+ )
29
+ end
30
+
31
+ it "does not find variables not in the template" do
32
+ env_hash["#{prefix}SAMPLE_WORLD"] = "this"
33
+ env_hash["#{prefix}OTHER_HELLO"] = "that"
34
+
35
+ assert_input_equals_hash({})
36
+ end
37
+
38
+ it "does not find variables with another prefix" do
39
+ env_hash["OTHER_SAMPLE_HELLO"] = "ok"
40
+
41
+ assert_input_equals_hash({})
42
+ end
43
+ end
44
+ end
45
+
46
+ describe "typecasting data from the system" do
47
+
48
+ it "converts data to the right type" do
49
+ env_hash["SAMPLE_NUMBER"] = "123"
50
+ env_hash["SAMPLE_NUMBER_TYPE"] = "integer"
51
+
52
+ assert_input_equals_hash(
53
+ sample: {
54
+ number: 123
55
+ }
56
+ )
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,33 @@
1
+ require 'helper'
2
+
3
+ describe Levels::Input::YAML do
4
+
5
+ let(:yaml_string) {
6
+ <<-STR
7
+ ---
8
+ group1:
9
+ key1: string
10
+ key2: 123
11
+ group2:
12
+ key:
13
+ - 1
14
+ - 2
15
+ - 3
16
+ STR
17
+ }
18
+
19
+ subject { Levels::Input::YAML.new(yaml_string) }
20
+
21
+ it "reads data from the JSON structure" do
22
+ assert_input_equals_hash(
23
+ group1: {
24
+ key1: "string",
25
+ key2: 123
26
+ },
27
+ group2: {
28
+ key: [1, 2, 3]
29
+ }
30
+ )
31
+ end
32
+ end
33
+
@@ -0,0 +1,45 @@
1
+ require 'helper'
2
+
3
+ describe Levels::Key do
4
+
5
+ let(:input) { "settings" }
6
+
7
+ subject { Levels::Key.new(input) }
8
+
9
+ specify "#to_sym" do
10
+ subject.to_sym.must_equal :settings
11
+ end
12
+
13
+ specify "#inspect" do
14
+ subject.inspect.must_equal "<Levels::Key :settings>"
15
+ end
16
+
17
+ specify "#to_s" do
18
+ subject.to_s.must_equal subject.inspect
19
+ end
20
+
21
+ describe "comparisons" do
22
+
23
+ let(:a) { Levels::Key.new(:a) }
24
+ let(:b) { Levels::Key.new(:a) }
25
+ let(:c) { Levels::Key.new(:b) }
26
+
27
+ specify "equality" do
28
+ (a == b).must_equal true
29
+ (b == a).must_equal true
30
+ (a == c).must_equal false
31
+ (a == :a).must_equal false
32
+ end
33
+
34
+ specify "hashing" do
35
+ hash = {}
36
+ hash[a] = true
37
+ hash[a].must_equal true
38
+ hash[b].must_equal true
39
+ hash[c].must_equal nil
40
+ hash[:a].must_equal nil
41
+ end
42
+ end
43
+ end
44
+
45
+
@@ -0,0 +1,106 @@
1
+ require 'helper'
2
+
3
+ describe Levels::KeyValues do
4
+
5
+ let(:data) { nil }
6
+
7
+ subject { Levels::KeyValues.new(data) }
8
+
9
+ describe "in general" do
10
+
11
+ let(:data) { { "a" => 1 } }
12
+
13
+ describe "#[]" do
14
+
15
+ it "handles string keys" do
16
+ subject["a"].must_equal 1
17
+ end
18
+
19
+ it "handles symbol keys" do
20
+ subject[:a].must_equal 1
21
+ end
22
+
23
+ it "returns nil when the key does not exist" do
24
+ subject[:b].must_equal nil
25
+ end
26
+ end
27
+
28
+ describe "#pair" do
29
+
30
+ it "returns a Key and the value" do
31
+ key, value = subject.pair(:a)
32
+ key.must_equal Levels::Key.new(:a)
33
+ value.must_equal 1
34
+ end
35
+
36
+ it "returns a Key and nil if the key does not exist" do
37
+ key, value = subject.pair(:b)
38
+ key.must_equal Levels::Key.new(:b)
39
+ value.must_equal nil
40
+ end
41
+ end
42
+
43
+ describe "#[]=" do
44
+
45
+ it "handles string keys" do
46
+ subject["a"] = 2
47
+ subject[:a].must_equal 2
48
+ end
49
+
50
+ it "handles Symbol keys" do
51
+ subject[:a] = 2
52
+ subject[:a].must_equal 2
53
+ end
54
+
55
+ it "can add new keys" do
56
+ subject[:b] = 1
57
+ subject[:b].must_equal 1
58
+ end
59
+ end
60
+
61
+ describe "#key?" do
62
+
63
+ it "handles string keys" do
64
+ subject.key?("a").must_equal true
65
+ end
66
+
67
+ it "handles symbol keys" do
68
+ subject.key?(:a).must_equal true
69
+ end
70
+
71
+ it "returns false if the key does not exist" do
72
+ subject.key?(:b).must_equal false
73
+ end
74
+ end
75
+
76
+ specify "#inspect" do
77
+ subject.inspect.must_equal "<Levels::KeyValues>"
78
+ end
79
+
80
+ specify "#to_s" do
81
+ subject.to_s.must_equal subject.inspect
82
+ end
83
+ end
84
+
85
+ describe "comparisons" do
86
+
87
+ let(:a) { Levels::KeyValues.new(a: 1) }
88
+ let(:b) { Levels::KeyValues.new(a: 1) }
89
+ let(:c) { Levels::KeyValues.new(b: 1) }
90
+
91
+ specify "equality" do
92
+ (a == b).must_equal true
93
+ (a == c).must_equal false
94
+ (a == {a:1}).must_equal false
95
+ end
96
+
97
+ specify "hashing" do
98
+ hash = {}
99
+ hash[a] = true
100
+ hash[a].must_equal true
101
+ hash[b].must_equal true
102
+ hash[c].must_equal nil
103
+ hash[{a:1}].must_equal nil
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,38 @@
1
+ require 'helper'
2
+
3
+ describe Levels::LazyEvaluator do
4
+
5
+ let(:level) { Levels::Level.new("level") }
6
+
7
+ subject { Levels::LazyEvaluator.new(level) }
8
+
9
+ it "returns a value" do
10
+ subject.call(123).must_equal 123
11
+ end
12
+
13
+ it "executes a proc" do
14
+ subject.call(-> { 123 }).must_equal 123
15
+ end
16
+
17
+ it "executes a proc recursively" do
18
+ subject.call(-> { -> { 123 } }).must_equal 123
19
+ end
20
+
21
+ it "allows the level to be accessed directly" do
22
+ level.set_group(:sample, message: "hello")
23
+ subject.call(-> { sample.message }).must_equal "hello"
24
+ subject.call(-> { sample[:message] }).must_equal "hello"
25
+ subject.call(-> { sample.defined?(:message) }).must_equal true
26
+ subject.call(-> { sample.message? }).must_equal true
27
+ end
28
+
29
+ it "evaluates values within an array" do
30
+ subject.call([-> { 123 }]).must_equal [123]
31
+ end
32
+
33
+ it "allows shell style interpolation" do
34
+ skip "for now"
35
+ level.set_group(:sample, message: "hello")
36
+ subject.call("${SAMPLE_HELLO} world").must_equal "hello world"
37
+ end
38
+ end
@@ -0,0 +1,89 @@
1
+ require 'helper'
2
+
3
+ describe Levels::Level do
4
+
5
+ subject { Levels::Level.new("test") }
6
+
7
+ specify "#_level_name" do
8
+ subject._level_name.must_equal "test"
9
+ end
10
+
11
+ specify "#to_s" do
12
+ subject.to_s.must_equal "<Levels::Level \"test\">"
13
+ end
14
+
15
+ it "allows groups to be defined and accessed" do
16
+ subject.set_group(:test, key: 123)
17
+ subject.test.must_be_instance_of Levels::Group
18
+ subject[:test].must_be_instance_of Levels::Group
19
+ end
20
+
21
+ it "handls a string group name" do
22
+ subject.set_group("test", key: 123)
23
+ subject.test.must_be_instance_of Levels::Group
24
+ subject[:test].must_be_instance_of Levels::Group
25
+ end
26
+
27
+ it "raises an error if you access an unknown group" do
28
+ proc { subject.nothing }.must_raise Levels::UnknownGroup
29
+ proc { subject[:nothing] }.must_raise Levels::UnknownGroup
30
+ end
31
+
32
+ it "allows the existence of a group to be tested" do
33
+ subject.set_group(:test, key: 123)
34
+ subject.defined?(:test).must_equal true
35
+ subject.defined?(:foo).must_equal false
36
+ subject.test?.must_equal true
37
+ subject.foo?.must_equal false
38
+ end
39
+
40
+ it "does not allow a group to be redefined" do
41
+ subject.set_group(:test, key: 123)
42
+ proc { subject.set_group(:test, key: 123) }.must_raise Levels::DuplicateGroup
43
+ end
44
+
45
+ describe "#to_enum" do
46
+
47
+ it "returns an Enumerator" do
48
+ subject.to_enum.must_be_instance_of Enumerator
49
+ end
50
+
51
+ it "iterates over all groups" do
52
+ subject.set_group(:test1, key: 123)
53
+ subject.set_group(:test2, key: 123)
54
+ result = subject.to_enum.map do |k, v|
55
+ [k, v.class]
56
+ end
57
+ expected = [
58
+ [:test1, Enumerator],
59
+ [:test2, Enumerator]
60
+ ]
61
+ result.sort.must_equal expected.sort
62
+ end
63
+ end
64
+
65
+ describe "#eql_hash?" do
66
+
67
+ let(:expected) {
68
+ {
69
+ test1: {
70
+ key: 123
71
+ },
72
+ "test2" => {
73
+ "key" => 123
74
+ }
75
+ }
76
+ }
77
+
78
+ before do
79
+ subject.set_group(:test1, key: 123)
80
+ subject.set_group(:test2, key: 123)
81
+ end
82
+
83
+ it "is true if the hash matches the data" do
84
+ subject.eql_hash?(expected).must_equal true
85
+ subject.eql_hash?(a: 1).must_equal false
86
+ end
87
+ end
88
+ end
89
+
@@ -0,0 +1,23 @@
1
+ require 'helper'
2
+
3
+ describe Levels do
4
+
5
+ specify ".setup" do
6
+ Levels.setup.must_be_instance_of Levels::Setup
7
+ end
8
+
9
+ specify ".merge" do
10
+ Levels.merge.must_be_instance_of Levels::Configuration
11
+ end
12
+
13
+ specify ".merge with a block" do
14
+ block_called = false
15
+ configuration = Levels.merge do |setup|
16
+ block_called = true
17
+ setup.must_be_instance_of Levels::Setup
18
+ end
19
+ block_called.must_equal true
20
+ configuration.must_be_instance_of Levels::Configuration
21
+ end
22
+ end
23
+
@@ -0,0 +1,55 @@
1
+ require 'helper'
2
+
3
+ describe Levels::Output::JSON do
4
+
5
+ let(:data) {
6
+ {
7
+ group1: {
8
+ key1: "string",
9
+ key2: 123
10
+ },
11
+ group2: {
12
+ key: [1, 2, 3]
13
+ }
14
+ }
15
+ }
16
+
17
+ let(:json_opts) { nil }
18
+
19
+ subject { Levels::Output::JSON.new(json_opts) }
20
+
21
+ def result
22
+ subject.generate(data.to_enum)
23
+ end
24
+
25
+ it "converts to JSON" do
26
+ result.must_equal <<-STR.chomp
27
+ {
28
+ "group1": {
29
+ "key1": "string",
30
+ "key2": 123
31
+ },
32
+ "group2": {
33
+ "key": [
34
+ 1,
35
+ 2,
36
+ 3
37
+ ]
38
+ }
39
+ }
40
+ STR
41
+ end
42
+
43
+
44
+ describe "initialized with json opts" do
45
+
46
+ let(:json_opts) { {} }
47
+
48
+ it "changes the formatting" do
49
+ result.must_equal <<-STR.chomp
50
+ {"group1":{"key1":"string","key2":123},"group2":{"key":[1,2,3]}}
51
+ STR
52
+ end
53
+ end
54
+ end
55
+