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.
- data/.gitignore +17 -0
- data/.rbenv-version +1 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile +12 -0
- data/Guardfile +14 -0
- data/LICENSE +22 -0
- data/README.md +315 -0
- data/Rakefile +28 -0
- data/bin/levels +130 -0
- data/examples/01_base.rb +6 -0
- data/examples/01_merge_to_json.sh +27 -0
- data/examples/01_prod.json +8 -0
- data/examples/02_base.rb +4 -0
- data/examples/02_merge_with_file.sh +20 -0
- data/examples/02_value +1 -0
- data/levels.gemspec +20 -0
- data/lib/levels.rb +77 -0
- data/lib/levels/audit.rb +24 -0
- data/lib/levels/audit/group_observer.rb +26 -0
- data/lib/levels/audit/nested_group_observer.rb +37 -0
- data/lib/levels/audit/root_observer.rb +63 -0
- data/lib/levels/audit/value.rb +64 -0
- data/lib/levels/audit/value_observer.rb +46 -0
- data/lib/levels/audit/values.rb +66 -0
- data/lib/levels/configuration.rb +98 -0
- data/lib/levels/configured_group.rb +62 -0
- data/lib/levels/event_handler.rb +127 -0
- data/lib/levels/group.rb +61 -0
- data/lib/levels/input/json.rb +17 -0
- data/lib/levels/input/ruby.rb +120 -0
- data/lib/levels/input/system.rb +63 -0
- data/lib/levels/input/yaml.rb +17 -0
- data/lib/levels/key.rb +28 -0
- data/lib/levels/key_values.rb +54 -0
- data/lib/levels/lazy_evaluator.rb +54 -0
- data/lib/levels/level.rb +80 -0
- data/lib/levels/method_missing.rb +14 -0
- data/lib/levels/output/json.rb +33 -0
- data/lib/levels/output/system.rb +29 -0
- data/lib/levels/output/yaml.rb +19 -0
- data/lib/levels/runtime.rb +30 -0
- data/lib/levels/setup.rb +132 -0
- data/lib/levels/system/constants.rb +8 -0
- data/lib/levels/system/key_formatter.rb +15 -0
- data/lib/levels/system/key_generator.rb +50 -0
- data/lib/levels/system/key_parser.rb +67 -0
- data/lib/levels/version.rb +3 -0
- data/test/acceptance/audit_test.rb +105 -0
- data/test/acceptance/event_handler_test.rb +43 -0
- data/test/acceptance/read_json_test.rb +35 -0
- data/test/acceptance/read_ruby_test.rb +117 -0
- data/test/acceptance/read_system_test.rb +121 -0
- data/test/acceptance/read_yaml_test.rb +38 -0
- data/test/acceptance/setup_test.rb +115 -0
- data/test/acceptance/write_json_test.rb +39 -0
- data/test/acceptance/write_system_test.rb +68 -0
- data/test/acceptance/write_yaml_test.rb +33 -0
- data/test/bin/merge_test.rb +194 -0
- data/test/bin/options_test.rb +41 -0
- data/test/helper.rb +12 -0
- data/test/support/acceptance_spec.rb +58 -0
- data/test/support/base_spec.rb +14 -0
- data/test/support/bin_spec.rb +65 -0
- data/test/support/tempfile_helper.rb +35 -0
- data/test/unit/audit/group_observer_test.rb +24 -0
- data/test/unit/audit/nested_group_observer_test.rb +28 -0
- data/test/unit/audit/root_observer_test.rb +54 -0
- data/test/unit/audit/value_observer_test.rb +63 -0
- data/test/unit/audit/value_test.rb +41 -0
- data/test/unit/audit/values_test.rb +86 -0
- data/test/unit/configuration_test.rb +72 -0
- data/test/unit/configured_group_test.rb +75 -0
- data/test/unit/group_test.rb +105 -0
- data/test/unit/input/json_test.rb +32 -0
- data/test/unit/input/ruby_test.rb +140 -0
- data/test/unit/input/system_test.rb +59 -0
- data/test/unit/input/yaml_test.rb +33 -0
- data/test/unit/key_test.rb +45 -0
- data/test/unit/key_values_test.rb +106 -0
- data/test/unit/lazy_evaluator_test.rb +38 -0
- data/test/unit/level_test.rb +89 -0
- data/test/unit/levels_test.rb +23 -0
- data/test/unit/output/json_test.rb +55 -0
- data/test/unit/output/system_test.rb +32 -0
- data/test/unit/output/yaml_test.rb +38 -0
- data/test/unit/runtime_test.rb +40 -0
- data/test/unit/system/key_formatter_test.rb +43 -0
- data/test/unit/system/key_generator_test.rb +21 -0
- data/test/unit/system/key_parser_test.rb +207 -0
- 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
|
+
|
data/test/helper.rb
ADDED
@@ -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
|