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,41 @@
1
+ require 'helper'
2
+
3
+ describe "bin: options" do
4
+
5
+ describe "--[no-]color" do
6
+
7
+ before do
8
+ w("one.rb", <<-RUBY)
9
+ group :sample
10
+ set message: "hello"
11
+ RUBY
12
+ end
13
+
14
+ it "can be turned on" do
15
+ assert_success "levels --color #{f 'one.rb'}"
16
+ stderr.must_match(Levels::Colorizer::FOREGROUND[:green])
17
+ end
18
+
19
+ it "can be turned off" do
20
+ assert_success "levels --no-color #{f 'one.rb'}"
21
+ stderr.wont_match(Levels::Colorizer::FOREGROUND[:green])
22
+ end
23
+ end
24
+
25
+ describe "--version" do
26
+
27
+ it "works" do
28
+ assert_success "levels --version"
29
+ stdout.must_equal "Levels #{Levels::VERSION}\n"
30
+ end
31
+ end
32
+
33
+ describe "--help" do
34
+
35
+ it "works" do
36
+ assert_success "levels --help"
37
+ stdout.split("\n")[0].must_equal "Usage: levels [options] [files]"
38
+ end
39
+ end
40
+ end
41
+
@@ -0,0 +1,12 @@
1
+ require 'minitest/spec'
2
+ require 'minitest/hell'
3
+ require 'minitest/autorun'
4
+ # fivemat isn't compatible with parallel tests right now
5
+ #require 'fivemat/minitest/autorun'
6
+
7
+ require 'levels'
8
+
9
+ require 'support/tempfile_helper'
10
+ require 'support/base_spec'
11
+ require 'support/acceptance_spec'
12
+ require 'support/bin_spec'
@@ -0,0 +1,58 @@
1
+ class AcceptanceSpec < MiniTest::Spec
2
+ include TempfileHelper
3
+
4
+ register_spec_type(self) do |desc|
5
+ desc =~ /^acceptance:/
6
+ end
7
+
8
+ def standard_data_types_level(name = :level)
9
+ level = Levels::Level.new("standard data types")
10
+ level.set_group(:types,
11
+ string: "hello",
12
+ integer: 123,
13
+ float: 1.5,
14
+ boolean_true: true,
15
+ boolean_false: false,
16
+ array_of_string: ["a", "b", "c"],
17
+ array_of_integer: [1, 2, 3],
18
+ null: nil
19
+ )
20
+ level.set_group(:group2,
21
+ message: "hello world"
22
+ )
23
+ level
24
+ end
25
+
26
+ def self.assert_sample_data_set
27
+
28
+ it "parses string" do
29
+ subject.types.string.must_equal "hello"
30
+ end
31
+
32
+ it "parses integer" do
33
+ subject.types.integer.must_equal 123
34
+ end
35
+
36
+ it "parses float" do
37
+ subject.types.float.must_equal 1.5
38
+ end
39
+
40
+ it "parses boolean" do
41
+ subject.types.boolean_true.must_equal true
42
+ subject.types.boolean_false.must_equal false
43
+ end
44
+
45
+ it "parses array" do
46
+ subject.types.array_of_string.must_equal ["a", "b", "c"]
47
+ subject.types.array_of_integer.must_equal [1, 2, 3]
48
+ end
49
+
50
+ it "parses null" do
51
+ subject.types.null.must_equal nil
52
+ end
53
+
54
+ it "parses all groups" do
55
+ subject.group2.message.must_equal "hello world"
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,14 @@
1
+ class BaseSpec < MiniTest::Spec
2
+
3
+ register_spec_type(self) { true }
4
+
5
+ # Verify that when an Input is read into a Level,
6
+ # that the Level matches the given Hash.
7
+ def assert_input_equals_hash(hash)
8
+ level = Levels::Level.new("Test")
9
+ subject.read(level)
10
+ level.eql_hash?(hash).must_equal true
11
+ end
12
+
13
+ end
14
+
@@ -0,0 +1,65 @@
1
+ require 'tmpdir'
2
+
3
+ class BinSpec < MiniTest::Spec
4
+ include TempfileHelper
5
+
6
+ register_spec_type(self) do |desc|
7
+ desc =~ /^bin:/
8
+ end
9
+
10
+ Result = Struct.new(:stdout, :stderr, :status)
11
+
12
+ def set_env(hash)
13
+ @command_env = hash
14
+ end
15
+
16
+ def assert_success(command)
17
+ args = [@command_env, command].compact
18
+ stdout, stderr, status = Open3.capture3(*args)
19
+ @result = Result.new(stdout, stderr, status.exitstatus)
20
+ if @result.status != 0
21
+ raise MiniTest::Assertion, "Status: #{@result.status}\n#{@result.stderr}"
22
+ end
23
+ end
24
+
25
+ def stdout
26
+ @result.stdout
27
+ end
28
+
29
+ def stderr
30
+ @result.stderr
31
+ end
32
+
33
+ def status
34
+ @result.status
35
+ end
36
+
37
+ # Create a temporary directory. This directory will exist for the life of
38
+ # the spec.
39
+ #
40
+ # id - Identifier of the tmpdir (default: the default identifier).
41
+ #
42
+ # Returns a Pathname.
43
+ def tmpdir(id=:default)
44
+ @tmpdirs[id] ||= Pathname.new(Dir.mktmpdir).realdirpath
45
+ end
46
+
47
+ # Get a file within the tmpdir.
48
+ def f(name)
49
+ tmpdir + name
50
+ end
51
+
52
+ # Write a file within the tmpdir.
53
+ def w(name, content)
54
+ f(name).open("w") { |f| f.puts content }
55
+ end
56
+
57
+ before do
58
+ @tmpdirs = {}
59
+ end
60
+
61
+ after do
62
+ @tmpdirs.values.each { |dir| FileUtils.rm_rf dir.to_s }
63
+ end
64
+
65
+ end
@@ -0,0 +1,35 @@
1
+ module TempfileHelper
2
+
3
+ # Create a temporary directory. This directory will exist for the life of
4
+ # the spec.
5
+ #
6
+ # id - Identifier of the tmpdir (default: the default identifier).
7
+ #
8
+ # Returns a Pathname.
9
+ def tmpdir(id=:default)
10
+ @tmpdirs[id] ||= Pathname.new(Dir.mktmpdir).realdirpath
11
+ end
12
+
13
+ # Get a file within the tmpdir.
14
+ def f(name)
15
+ tmpdir + name
16
+ end
17
+
18
+ # Write a file within the tmpdir.
19
+ def w(name, content)
20
+ f(name).open("w") { |f| f.print content }
21
+ f(name)
22
+ end
23
+
24
+ def self.included(base)
25
+ base.class_eval do
26
+ before do
27
+ @tmpdirs = {}
28
+ end
29
+ after do
30
+ @tmpdirs.values.each { |dir| FileUtils.rm_rf dir.to_s }
31
+ end
32
+ end
33
+ end
34
+
35
+ end
@@ -0,0 +1,24 @@
1
+ require 'helper'
2
+
3
+ describe Levels::Audit::GroupObserver do
4
+
5
+ let(:value_observer) { MiniTest::Mock.new }
6
+ let(:user_observer) { MiniTest::Mock.new }
7
+
8
+ subject { Levels::Audit::GroupObserver.new(value_observer, user_observer) }
9
+
10
+ it "observes values by finding them and notifying what it found" do
11
+ levels = MiniTest::Mock.new
12
+ group_key = :group
13
+ value_key = :value
14
+
15
+ observed_values = "the values"
16
+ value_observer.expect(:observe_values, observed_values, [levels, group_key, value_key])
17
+ user_observer.expect(:on_values, nil, [observed_values])
18
+
19
+ returned_values = subject.observe_values(levels, group_key, value_key)
20
+ returned_values.must_equal observed_values
21
+ value_observer.verify
22
+ user_observer.verify
23
+ end
24
+ end
@@ -0,0 +1,28 @@
1
+ require 'helper'
2
+
3
+ describe Levels::Audit::NestedGroupObserver do
4
+
5
+ let(:value_observer) { MiniTest::Mock.new }
6
+
7
+ subject { Levels::Audit::NestedGroupObserver.new(value_observer) }
8
+
9
+ it "observes values by collecting them, then notifying when asked" do
10
+ levels = MiniTest::Mock.new
11
+ group_key = :group
12
+ value_key = :value
13
+
14
+ observed_values = "the values"
15
+ value_observer.expect(:observe_values, observed_values, [levels, group_key, value_key])
16
+
17
+ returned_values = subject.observe_values(levels, group_key, value_key)
18
+ returned_values.must_equal observed_values
19
+ value_observer.verify
20
+
21
+ user_observer = MiniTest::Mock.new
22
+ user_observer.expect(:on_nested_values, nil, [observed_values])
23
+
24
+ subject.notify_nested(user_observer)
25
+ user_observer.verify
26
+ end
27
+ end
28
+
@@ -0,0 +1,54 @@
1
+ require 'helper'
2
+
3
+ describe Levels::Audit::RootObserver do
4
+
5
+ let(:lazy_evaluator) { MiniTest::Mock.new }
6
+ let(:user_observer) { MiniTest::Mock.new }
7
+
8
+ subject { Levels::Audit::RootObserver.new(lazy_evaluator) }
9
+
10
+ describe "#observe_group without a current value" do
11
+
12
+ it "returns a group observer" do
13
+ group_observer = subject.observe_group(user_observer)
14
+ group_observer.must_be_instance_of Levels::Audit::GroupObserver
15
+ end
16
+ end
17
+
18
+ describe "#with_current_value" do
19
+
20
+ it "sets the current value for the life of the block, and restores each previous value" do
21
+ value1 = "the value 1"
22
+ value2 = "the value 2"
23
+
24
+ subject.current_value.must_be_nil
25
+
26
+ subject.with_current_value(value1) do
27
+ subject.current_value.must_equal value1
28
+
29
+ subject.with_current_value(value2) do
30
+ subject.current_value.must_equal value2
31
+ end
32
+
33
+ subject.current_value.must_equal value1
34
+ end
35
+
36
+ subject.current_value.must_be_nil
37
+ end
38
+ end
39
+
40
+ describe "#observe_group with a current value" do
41
+
42
+ it "returns a nested group observer, and adds that observer to the current value" do
43
+ value = MiniTest::Mock.new
44
+ value.expect(:add_nested_group_observer, nil, [Levels::Audit::NestedGroupObserver])
45
+
46
+ group_observer = nil
47
+ subject.with_current_value(value) do
48
+ group_observer = subject.observe_group(user_observer)
49
+ end
50
+
51
+ group_observer.must_be_instance_of Levels::Audit::NestedGroupObserver
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,63 @@
1
+ require 'helper'
2
+
3
+ describe Levels::Audit::ValueObserver do
4
+
5
+ let(:root_observer_class) {
6
+ Class.new do
7
+
8
+ def initialize
9
+ @values = []
10
+ end
11
+
12
+ def with_current_value(value)
13
+ @values << value
14
+ yield
15
+ end
16
+
17
+ def raw_values
18
+ @values.map { |v| v.raw }
19
+ end
20
+ end
21
+ }
22
+
23
+ let(:root_observer) { root_observer_class.new }
24
+ let(:lazy_evaluator) { MiniTest::Mock.new }
25
+
26
+ after do
27
+ lazy_evaluator.verify
28
+ end
29
+
30
+ subject { Levels::Audit::ValueObserver.new(root_observer, lazy_evaluator) }
31
+
32
+ it "traverses all levels to find values" do
33
+ level1 = Levels::Level.new("one")
34
+ level1.set_group(:group, a: 1)
35
+
36
+ level2 = Levels::Level.new("two")
37
+ level2.set_group(:group, a: 2)
38
+
39
+ level3 = Levels::Level.new("two")
40
+ level3.set_group(:group, b: 0)
41
+
42
+ level4 = Levels::Level.new("two")
43
+
44
+ # A Level for each case, and two with applicable values.
45
+ levels = [level1, level2, level3, level4]
46
+
47
+ # Translate the initial values into something else.
48
+ lazy_evaluator.expect(:call, 11, [1])
49
+ lazy_evaluator.expect(:call, 22, [2])
50
+
51
+ values = subject.observe_values(levels, :group, :a)
52
+
53
+ values.must_be_instance_of Levels::Audit::Values
54
+ values.group_key.must_equal :group
55
+ values.value_key.must_equal :a
56
+
57
+ # The observer was passed each Value
58
+ root_observer.raw_values.must_equal [11, 22]
59
+
60
+ # The returned Values is made up of each Value.
61
+ values.map { |v| v.raw }.must_equal [11, 22]
62
+ end
63
+ end
@@ -0,0 +1,41 @@
1
+ require 'helper'
2
+
3
+ describe Levels::Audit::Value do
4
+
5
+ let(:level_name) { "My Level" }
6
+ let(:final) { true }
7
+ let(:value) { "hello" }
8
+
9
+ subject { Levels::Audit::Value.new(level_name, final) { value } }
10
+
11
+ it "exposes the level_name" do
12
+ subject.level_name.must_equal level_name
13
+ end
14
+
15
+ it "exposes the value" do
16
+ subject.value.must_equal "hello"
17
+ end
18
+
19
+ it "exposes the value as `raw`" do
20
+ subject.raw.must_equal "hello"
21
+ end
22
+
23
+ it "inspect's as the value" do
24
+ subject.inspect.must_equal %("hello")
25
+ end
26
+
27
+ it "expose finality" do
28
+ a = Levels::Audit::Value.new(level_name, true) { value }
29
+ b = Levels::Audit::Value.new(level_name, false) { value }
30
+ a.must_be :final?
31
+ b.wont_be :final?
32
+ end
33
+
34
+ it "exposes recursiveness" do
35
+ subject.wont_be :recursive?
36
+
37
+ subject.add_nested_group_observer(MiniTest::Mock.new)
38
+
39
+ subject.must_be :recursive?
40
+ end
41
+ end