levels 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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