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