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,86 @@
1
+ require 'helper'
2
+
3
+ describe Levels::Audit::Values do
4
+
5
+ let(:group_key) { :group }
6
+ let(:value_key) { :value }
7
+ let(:values) { [] }
8
+
9
+ subject { Levels::Audit::Values.new(group_key, value_key, values) }
10
+
11
+ it "exposes the group_key" do
12
+ subject.group_key.must_equal group_key
13
+ end
14
+
15
+ it "exposes the value_key" do
16
+ subject.value_key.must_equal value_key
17
+ end
18
+
19
+ specify "#final" do
20
+ v1 = Levels::Audit::Value.new("a", false)
21
+ v2 = Levels::Audit::Value.new("a", true)
22
+
23
+ values.concat [v1, v2]
24
+
25
+ subject.final.must_equal v2
26
+ end
27
+
28
+ specify "#final_value" do
29
+ v1 = Levels::Audit::Value.new("a", false, "no")
30
+ v2 = Levels::Audit::Value.new("a", true, "hello")
31
+
32
+ values.concat [v1, v2]
33
+
34
+ subject.final_value.must_equal "hello"
35
+ end
36
+
37
+ specify "#each is Enumerable" do
38
+ subject.map.must_be_instance_of Enumerator
39
+ end
40
+
41
+ specify "#only_final?" do
42
+ v1 = MiniTest::Mock.new
43
+ v1.expect(:final?, false)
44
+ v2 = MiniTest::Mock.new
45
+ v2.expect(:final?, true)
46
+
47
+ subject.wont_be :only_final?
48
+
49
+ values << v2
50
+ subject.must_be :only_final?
51
+
52
+ values << v1
53
+ subject.wont_be :only_final?
54
+ end
55
+
56
+ specify "#recursive?" do
57
+ v1 = MiniTest::Mock.new
58
+ 2.times { v1.expect(:recursive?, false) }
59
+ v2 = MiniTest::Mock.new
60
+ v2.expect(:recursive?, true)
61
+
62
+ subject.wont_be :recursive?
63
+
64
+ values << v1
65
+ subject.wont_be :recursive?
66
+
67
+ values << v2
68
+ subject.must_be :recursive?
69
+ end
70
+
71
+ specify "#size" do
72
+ values.size.must_equal 0
73
+
74
+ values << MiniTest::Mock.new
75
+
76
+ values.size.must_equal 1
77
+ end
78
+
79
+ specify "#empty?" do
80
+ values.must_be :empty?
81
+
82
+ values << MiniTest::Mock.new
83
+
84
+ values.wont_be :empty?
85
+ end
86
+ end
@@ -0,0 +1,72 @@
1
+ require 'helper'
2
+
3
+ describe Levels::Configuration do
4
+
5
+ let(:level1) { Levels::Level.new("l1") }
6
+ let(:level2) { Levels::Level.new("l2") }
7
+
8
+ subject { Levels::Configuration.new([level1, level2]) }
9
+
10
+ before do
11
+ level1.set_group(:g1, a: 1)
12
+
13
+ level2.set_group(:g1, a: 9)
14
+ level2.set_group(:g2, b: 2)
15
+ end
16
+
17
+ specify "#to_s" do
18
+ subject.to_s.must_equal "<Levels::Configuration l1, l2>"
19
+ end
20
+
21
+ it "allows groups to be retrieved" do
22
+ subject.g1.must_be_instance_of Levels::ConfiguredGroup
23
+ subject[:g1].must_be_instance_of Levels::ConfiguredGroup
24
+ end
25
+
26
+ it "initializes the configured group with the right levels" do
27
+ subject.g1.a.must_equal 9
28
+ subject.g1.b?.must_equal false
29
+
30
+ subject.g2.b.must_equal 2
31
+ subject.g2.a?.must_equal false
32
+ end
33
+
34
+ it "raises an error if you access an unknown group" do
35
+ proc { subject.nothing }.must_raise Levels::UnknownGroup
36
+ proc { subject[:nothing] }.must_raise Levels::UnknownGroup
37
+ end
38
+
39
+ it "allows the existence of a group to be tested" do
40
+ subject.defined?(:g1).must_equal true
41
+ subject.defined?(:g2).must_equal true
42
+ subject.defined?(:foo).must_equal false
43
+ subject.g1?.must_equal true
44
+ subject.g2?.must_equal true
45
+ subject.foo?.must_equal false
46
+ end
47
+
48
+ it "allows lazy evaluation" do
49
+ level2.set_group(:g3, sum: -> { g1.a + g2.b })
50
+ subject.g3.sum.must_equal 11
51
+ end
52
+
53
+ describe "#to_enum" do
54
+
55
+ it "returns an Enumerator" do
56
+ subject.to_enum.must_be_instance_of Enumerator
57
+ end
58
+
59
+ it "iterates over all groups" do
60
+ result = subject.to_enum.map do |k, v|
61
+ [k.to_sym, v.class]
62
+ end
63
+ expected = [
64
+ [:g1, Enumerator],
65
+ [:g2, Enumerator]
66
+ ]
67
+ result.sort.must_equal expected.sort
68
+ end
69
+ end
70
+ end
71
+
72
+
@@ -0,0 +1,75 @@
1
+ require 'helper'
2
+
3
+ describe Levels::ConfiguredGroup do
4
+
5
+ let(:levels) { [] }
6
+ let(:group_observer) { MiniTest::Mock.new }
7
+
8
+ subject { Levels::ConfiguredGroup.new(levels, :test, group_observer) }
9
+
10
+ def define_value(key, value)
11
+ level = Levels::Level.new("one")
12
+ level.set_group(:test, key => value)
13
+ levels << level
14
+ end
15
+
16
+ def observe_value(key, value)
17
+ observed_values = MiniTest::Mock.new
18
+ observed_values.expect(:final_value, value)
19
+ group_observer.expect(:observe_values, observed_values, [levels, :test, key])
20
+ end
21
+
22
+ describe "checking value existence" do
23
+
24
+ before do
25
+ define_value :a, nil
26
+ end
27
+
28
+ it "allows defined?" do
29
+ subject.defined?(:a).must_equal true
30
+ end
31
+
32
+ it "allows method access" do
33
+ subject.a?.must_equal true
34
+ end
35
+ end
36
+
37
+ describe "reading values" do
38
+
39
+ before do
40
+ define_value :a, nil
41
+ observe_value :a, 1
42
+ end
43
+
44
+ it "allows hash access to any key" do
45
+ subject[:a].must_equal 1
46
+ end
47
+
48
+ it "allows method access" do
49
+ subject.a.must_equal 1
50
+ end
51
+ end
52
+
53
+ describe "#to_enum" do
54
+
55
+ before do
56
+ define_value :a, nil
57
+ observe_value :a, 1
58
+ end
59
+
60
+ it "returns an Enumerator" do
61
+ subject.to_enum.must_be_instance_of Enumerator
62
+ end
63
+
64
+ it "iterates over all keys and values" do
65
+ result = subject.to_enum.map do |k, v|
66
+ [k.to_sym, v]
67
+ end
68
+ expected = [
69
+ [:a, 1],
70
+ ]
71
+ result.sort.must_equal expected.sort
72
+ end
73
+ end
74
+ end
75
+
@@ -0,0 +1,105 @@
1
+ require 'helper'
2
+
3
+ describe Levels::Group do
4
+
5
+ let(:hash) { {} }
6
+ let(:value_transformer) { nil }
7
+
8
+ subject { Levels::Group.new(hash, value_transformer) }
9
+
10
+ before do
11
+ hash[:name] = "ok"
12
+ hash[:value] = 123
13
+ hash[:other] = nil
14
+ end
15
+
16
+ specify "#to_s" do
17
+ subject.to_s.must_equal "<Levels::Group>"
18
+ end
19
+
20
+ it "allows hash access" do
21
+ subject[:name].must_equal "ok"
22
+ subject[:value].must_equal 123
23
+ subject[:other].must_equal nil
24
+ end
25
+
26
+ it "allows dot access" do
27
+ subject.name.must_equal "ok"
28
+ subject.value.must_equal 123
29
+ subject.other.must_equal nil
30
+ end
31
+
32
+ it "raises an error if you access a nonexistent key" do
33
+ proc { subject[:foo] }.must_raise Levels::UnknownKey
34
+ proc { subject.foo }.must_raise Levels::UnknownKey
35
+ end
36
+
37
+ it "allows the existence of a key to be tested" do
38
+ subject.defined?(:name).must_equal true
39
+ subject.defined?(:foo).must_equal false
40
+ subject.name?.must_equal true
41
+ subject.foo?.must_equal false
42
+ end
43
+
44
+ it "makes sure you don't call it wrong" do
45
+ proc { subject.name("ok") }.must_raise ArgumentError
46
+ end
47
+
48
+ describe "initialized with string keys" do
49
+
50
+ it "converts everything to symbols" do
51
+ hash["string"] = 123
52
+ subject.string.must_equal 123
53
+ subject[:string].must_equal 123
54
+ end
55
+ end
56
+
57
+ describe "initialized with a value transformer" do
58
+
59
+ let(:value_transformer) {
60
+ -> key, value { [key, value] }
61
+ }
62
+
63
+ it "returns the value via the transformer" do
64
+ subject.name.must_equal [:name, "ok"]
65
+ subject[:name].must_equal [:name, "ok"]
66
+ subject["name"].must_equal [:name, "ok"]
67
+ end
68
+ end
69
+
70
+ describe "#to_enum" do
71
+
72
+ it "returns an Enumerator" do
73
+ subject.to_enum.must_be_instance_of Enumerator
74
+ end
75
+
76
+ it "iterates over all keys and values" do
77
+ result = subject.to_enum.map do |k, v|
78
+ [k, v]
79
+ end
80
+ expected = [
81
+ [:name, "ok"],
82
+ [:value, 123],
83
+ [:other, nil]
84
+ ]
85
+ result.sort.must_equal expected.sort
86
+ end
87
+ end
88
+
89
+ describe "#eql_hash?" do
90
+
91
+ let(:expected) {
92
+ {
93
+ name: "ok",
94
+ value: 123,
95
+ "other" => nil
96
+ }
97
+ }
98
+
99
+ it "is true if the hash matches the data" do
100
+ subject.eql_hash?(expected).must_equal true
101
+ subject.eql_hash?(a: 1).must_equal false
102
+ end
103
+ end
104
+ end
105
+
@@ -0,0 +1,32 @@
1
+ require 'helper'
2
+
3
+ describe Levels::Input::JSON do
4
+
5
+ let(:json_string) {
6
+ <<-STR
7
+ {
8
+ "group1": {
9
+ "key1": "string",
10
+ "key2": 123
11
+ },
12
+ "group2": {
13
+ "key": [1, 2, 3]
14
+ }
15
+ }
16
+ STR
17
+ }
18
+
19
+ subject { Levels::Input::JSON.new(json_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
@@ -0,0 +1,140 @@
1
+ require 'helper'
2
+
3
+ describe Levels::Input::Ruby do
4
+
5
+ let(:level) { Levels::Level.new("Ruby") }
6
+
7
+ subject { Levels::Input::Ruby.new(ruby_string, "file.rb", 1) }
8
+
9
+ describe "successfully" do
10
+
11
+ let(:ruby_string) {
12
+ <<-RUBY
13
+ group :foo
14
+ set value: "ok"
15
+ RUBY
16
+ }
17
+
18
+ it "populates the level" do
19
+ subject.read(level)
20
+ level.eql_hash?(
21
+ foo: {
22
+ value: "ok"
23
+ }
24
+ ).must_equal true
25
+ end
26
+ end
27
+
28
+ describe "with an error" do
29
+
30
+ let(:ruby_string) {
31
+ <<-RUBY
32
+ xgroup :foo
33
+ set value: "ok"
34
+ RUBY
35
+ }
36
+
37
+ it "provides useful information" do
38
+ begin
39
+ subject.read(level)
40
+ rescue => e
41
+ e.class.must_equal NoMethodError
42
+ e.message.must_equal %(undefined method `xgroup' for <Levels>:Levels::Input::Ruby::DSL)
43
+ e.backtrace.first.must_equal "file.rb:1:in `read'"
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ describe Levels::Input::Ruby::DSL do
50
+
51
+ let(:level) { Levels::Level.new("Ruby") }
52
+
53
+ subject { Levels::Input::Ruby::DSL.new(level) }
54
+
55
+ def define(&block)
56
+ subject.instance_eval(&block)
57
+ subject.close_current_group
58
+ end
59
+
60
+ it "defines an empty group" do
61
+ define do
62
+ group :one
63
+ end
64
+ level.defined?(:one).must_equal true
65
+ end
66
+
67
+ it "defines a group with keys" do
68
+ define do
69
+ group :one
70
+ set key: "value"
71
+ end
72
+ level.one.key.must_equal "value"
73
+ end
74
+
75
+ it "is an error to set a key outside of a group" do
76
+ -> {
77
+ define do
78
+ set key: "value"
79
+ end
80
+ }.must_raise(SyntaxError)
81
+ end
82
+
83
+ it "is an error to set a key more than once" do
84
+ -> {
85
+ define do
86
+ group :one
87
+ set key: "value"
88
+ set "key" => "value"
89
+ end
90
+ }.must_raise(RuntimeError)
91
+ end
92
+
93
+ it "is an error to define a group more than once" do
94
+ -> {
95
+ subject.group :one
96
+ subject.close_current_group
97
+ subject.group "one"
98
+ }.must_raise(RuntimeError)
99
+ end
100
+
101
+ describe "the ways to set a value" do
102
+
103
+ before do
104
+ subject.group :one
105
+ end
106
+
107
+ def assert_key
108
+ subject.close_current_group
109
+ level.one.key.must_equal "value"
110
+ end
111
+
112
+ it "can be set as a ruby 1.9 hash" do
113
+ subject.set key: "value"
114
+ assert_key
115
+ end
116
+
117
+ it "can be set as a ruby 1.8 hash" do
118
+ subject.set :key => "value"
119
+ assert_key
120
+ end
121
+
122
+ it "is possible to set multiple keys via a hash" do
123
+ subject.set :key => "value", :bar => "foo"
124
+ assert_key
125
+ level.one.bar.must_equal "foo"
126
+ end
127
+
128
+ it "can be set with two arguments" do
129
+ subject.set :key, "value"
130
+ assert_key
131
+ end
132
+
133
+ it "is an error to pass other types of args" do
134
+ -> { subject.set :a }.must_raise(ArgumentError)
135
+ -> { subject.set :a, 1, :b }.must_raise(ArgumentError)
136
+ -> { subject.set :a, 1, :b, 2 }.must_raise(ArgumentError)
137
+ -> { subject.set :b, :a => 1 }.must_raise(ArgumentError)
138
+ end
139
+ end
140
+ end