light-service 0.3.6 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +66 -33
- data/RELEASES.md +39 -0
- data/lib/light-service.rb +6 -0
- data/lib/light-service/configuration.rb +19 -0
- data/lib/light-service/context_key_verifier.rb +8 -2
- data/lib/light-service/organizer.rb +8 -8
- data/lib/light-service/organizer/with_reducer.rb +21 -0
- data/lib/light-service/organizer/with_reducer_factory.rb +14 -0
- data/lib/light-service/organizer/with_reducer_log_decorator.rb +65 -0
- data/lib/light-service/version.rb +1 -1
- data/spec/acceptance/add_numbers_spec.rb +7 -8
- data/spec/acceptance/log_from_organizer_spec.rb +116 -0
- data/spec/action_expected_keys_spec.rb +25 -45
- data/spec/action_expects_and_promises_spec.rb +68 -70
- data/spec/action_promised_keys_spec.rb +53 -64
- data/spec/action_spec.rb +45 -58
- data/spec/context_spec.rb +86 -87
- data/spec/organizer_spec.rb +12 -29
- data/spec/sample/calculates_tax_spec.rb +2 -1
- data/spec/test_doubles.rb +132 -0
- metadata +11 -2
@@ -1,55 +1,35 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'test_doubles'
|
2
3
|
|
3
|
-
|
4
|
-
describe ":expects macro" do
|
5
|
-
class DummyActionForKeysToExpect
|
6
|
-
include LightService::Action
|
7
|
-
expects :tea, :milk
|
8
|
-
promises :milk_tea
|
4
|
+
describe ":expects macro" do
|
9
5
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
end
|
14
|
-
|
15
|
-
context "when expected keys are in the context" do
|
16
|
-
it "can access the keys as class methods" do
|
17
|
-
resulting_context = DummyActionForKeysToExpect.execute(
|
18
|
-
:tea => "black",
|
19
|
-
:milk => "full cream",
|
20
|
-
:something => "else"
|
21
|
-
)
|
22
|
-
expect(resulting_context[:milk_tea]).to eq("black - full cream")
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
context "when an expected key is not in the context" do
|
27
|
-
it "raises an error" do
|
28
|
-
exception_error_text = "expected :milk to be in the context during LightService::DummyActionForKeysToExpect"
|
29
|
-
expect {
|
30
|
-
DummyActionForKeysToExpect.execute(:tea => "black")
|
31
|
-
}.to raise_error(ExpectedKeysNotInContextError, exception_error_text)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
it "can collect expected keys when the `expects` macro is called multiple times" do
|
36
|
-
class DummyActionWithMultipleExpects
|
37
|
-
include LightService::Action
|
38
|
-
expects :tea
|
39
|
-
expects :milk, :chocolate
|
40
|
-
promises :milk_tea
|
41
|
-
|
42
|
-
executed do |context|
|
43
|
-
context.milk_tea = "#{context.tea} - #{context.milk} - with #{context.chocolate}"
|
44
|
-
end
|
45
|
-
end
|
46
|
-
resulting_context = DummyActionWithMultipleExpects.execute(
|
6
|
+
context "when expected keys are in the context" do
|
7
|
+
it "can access the keys as class methods" do
|
8
|
+
resulting_context = TestDoubles::MakesTeaWithMilkAction.execute(
|
47
9
|
:tea => "black",
|
48
10
|
:milk => "full cream",
|
49
|
-
:
|
11
|
+
:something => "else"
|
50
12
|
)
|
51
|
-
expect(resulting_context[:milk_tea]).to eq("black - full cream
|
13
|
+
expect(resulting_context[:milk_tea]).to eq("black - full cream")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context "when an expected key is not in the context" do
|
18
|
+
it "raises an LightService::ExpectedKeysNotInContextError" do
|
19
|
+
exception_error_text = "expected :milk to be in the context during TestDoubles::MakesTeaWithMilkAction"
|
20
|
+
expect {
|
21
|
+
TestDoubles::MakesTeaWithMilkAction.execute(:tea => "black")
|
22
|
+
}.to raise_error(LightService::ExpectedKeysNotInContextError, exception_error_text)
|
52
23
|
end
|
24
|
+
end
|
53
25
|
|
26
|
+
it "can collect expected keys when the `expects` macro is called multiple times" do
|
27
|
+
resulting_context = TestDoubles::MultipleExpectsAction.execute(
|
28
|
+
:tea => "black",
|
29
|
+
:milk => "full cream",
|
30
|
+
:chocolate => "dark chocolate"
|
31
|
+
)
|
32
|
+
expect(resulting_context[:milk_tea]).to eq("black - full cream - with dark chocolate")
|
54
33
|
end
|
34
|
+
|
55
35
|
end
|
@@ -1,95 +1,93 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
|
4
|
-
describe "
|
5
|
-
|
6
|
-
|
7
|
-
include LightService::Action
|
3
|
+
describe ":expects and :promises macros" do
|
4
|
+
describe "actions are backward compatible" do
|
5
|
+
class FooAction
|
6
|
+
include LightService::Action
|
8
7
|
|
9
|
-
|
10
|
-
|
8
|
+
executed do |context|
|
9
|
+
baz = context.fetch :baz
|
11
10
|
|
12
|
-
|
13
|
-
|
14
|
-
end
|
15
|
-
end
|
16
|
-
it "works without expects and promises" do
|
17
|
-
result = FooAction.execute(:baz => 3)
|
18
|
-
expect(result).to be_success
|
19
|
-
expect(result[:bar]).to eq(5)
|
11
|
+
bar = baz + 2
|
12
|
+
context[:bar] = bar
|
20
13
|
end
|
21
14
|
end
|
15
|
+
it "works without expects and promises" do
|
16
|
+
result = FooAction.execute(:baz => 3)
|
17
|
+
expect(result).to be_success
|
18
|
+
expect(result[:bar]).to eq(5)
|
19
|
+
end
|
20
|
+
end
|
22
21
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
22
|
+
context "when expected keys are not in context" do
|
23
|
+
class FooNoExpectedKeyAction
|
24
|
+
include LightService::Action
|
25
|
+
expects :baz
|
27
26
|
|
28
|
-
|
29
|
-
|
27
|
+
executed do |context|
|
28
|
+
baz = context.fetch :baz
|
30
29
|
|
31
|
-
|
32
|
-
|
33
|
-
end
|
34
|
-
end
|
35
|
-
it "throws an ExpectedKeysNotInContextError" do
|
36
|
-
# FooAction invoked with nothing in the context
|
37
|
-
expect { FooNoExpectedKeyAction.execute }.to \
|
38
|
-
raise_error(ExpectedKeysNotInContextError)
|
30
|
+
bar = baz + 2
|
31
|
+
context[:bar] = bar
|
39
32
|
end
|
40
33
|
end
|
34
|
+
it "throws an ExpectedKeysNotInContextError" do
|
35
|
+
# FooAction invoked with nothing in the context
|
36
|
+
expect { FooNoExpectedKeyAction.execute }.to \
|
37
|
+
raise_error(LightService::ExpectedKeysNotInContextError)
|
38
|
+
end
|
39
|
+
end
|
41
40
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
41
|
+
describe "expected keys" do
|
42
|
+
class FooWithReaderAction
|
43
|
+
include LightService::Action
|
44
|
+
expects :baz
|
46
45
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
end
|
52
|
-
end
|
53
|
-
it "can be accessed through a reader" do
|
54
|
-
result = FooWithReaderAction.execute(:baz => 3)
|
55
|
-
expect(result).to be_success
|
56
|
-
expect(result[:bar]).to eq(5)
|
46
|
+
executed do |context|
|
47
|
+
# Notice how I use `context.baz` here
|
48
|
+
bar = context.baz + 2
|
49
|
+
context[:bar] = bar
|
57
50
|
end
|
58
51
|
end
|
52
|
+
it "can be accessed through a reader" do
|
53
|
+
result = FooWithReaderAction.execute(:baz => 3)
|
54
|
+
expect(result).to be_success
|
55
|
+
expect(result[:bar]).to eq(5)
|
56
|
+
end
|
57
|
+
end
|
59
58
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
59
|
+
context "when promised keys are not in context" do
|
60
|
+
class FooNoPromisedKeyAction
|
61
|
+
include LightService::Action
|
62
|
+
expects :baz
|
63
|
+
promises :bar
|
65
64
|
|
66
|
-
|
67
|
-
|
68
|
-
end
|
69
|
-
end
|
70
|
-
it "throws a PromisedKeysNotInContextError" do
|
71
|
-
# FooAction invoked with nothing placed in the context
|
72
|
-
expect { FooNoPromisedKeyAction.execute(:baz => 3) }.to \
|
73
|
-
raise_error(PromisedKeysNotInContextError)
|
65
|
+
executed do |context|
|
66
|
+
# I am not adding anything to the context
|
74
67
|
end
|
75
68
|
end
|
69
|
+
it "throws a PromisedKeysNotInContextError" do
|
70
|
+
# FooAction invoked with nothing placed in the context
|
71
|
+
expect { FooNoPromisedKeyAction.execute(:baz => 3) }.to \
|
72
|
+
raise_error(LightService::PromisedKeysNotInContextError)
|
73
|
+
end
|
74
|
+
end
|
76
75
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
76
|
+
describe "promised keys" do
|
77
|
+
class FooWithExpectsAndPromisesAction
|
78
|
+
include LightService::Action
|
79
|
+
expects :baz
|
80
|
+
promises :bar
|
82
81
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
end
|
87
|
-
end
|
88
|
-
it "puts the value through the accessor into the context" do
|
89
|
-
result = FooWithExpectsAndPromisesAction.execute(:baz => 3)
|
90
|
-
expect(result).to be_success
|
91
|
-
expect(result[:bar]).to eq(5)
|
82
|
+
executed do |context|
|
83
|
+
# Notice how I use `context.bar` here
|
84
|
+
context.bar = context.baz + 2
|
92
85
|
end
|
93
86
|
end
|
87
|
+
it "puts the value through the accessor into the context" do
|
88
|
+
result = FooWithExpectsAndPromisesAction.execute(:baz => 3)
|
89
|
+
expect(result).to be_success
|
90
|
+
expect(result[:bar]).to eq(5)
|
91
|
+
end
|
94
92
|
end
|
95
93
|
end
|
@@ -1,88 +1,77 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'test_doubles'
|
2
3
|
|
3
|
-
|
4
|
-
describe ":promises macro" do
|
5
|
-
class DummyActionForKeysToPromise
|
6
|
-
include LightService::Action
|
7
|
-
expects :tea, :milk
|
8
|
-
promises :milk_tea
|
4
|
+
describe ":promises macro" do
|
9
5
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
executed do |context|
|
16
|
-
context[:some_tea] = "#{context.tea} - #{context.milk}"
|
17
|
-
end
|
6
|
+
context "when the promised key is not in the context" do
|
7
|
+
it "raises an ArgumentError" do
|
8
|
+
class TestDoubles::MakesCappuccinoAction
|
9
|
+
executed do |context|
|
10
|
+
context[:macchiato] = "#{context.coffee} - #{context.milk}"
|
18
11
|
end
|
19
|
-
|
20
|
-
exception_error_text = "promised :milk_tea to be in the context during LightService::DummyActionForKeysToPromise"
|
21
|
-
expect {
|
22
|
-
DummyActionForKeysToPromise.execute(:tea => "black", :milk => "full cream")
|
23
|
-
}.to raise_error(PromisedKeysNotInContextError, exception_error_text)
|
24
12
|
end
|
25
13
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
14
|
+
exception_error_text = "promised :cappuccino to be in the context during TestDoubles::MakesCappuccinoAction"
|
15
|
+
expect {
|
16
|
+
TestDoubles::MakesCappuccinoAction.execute(:coffee => "espresso", :milk => "2%")
|
17
|
+
}.to raise_error(LightService::PromisedKeysNotInContextError, exception_error_text)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "can fail the context without fulfilling its promise" do
|
21
|
+
class TestDoubles::MakesCappuccinoAction
|
22
|
+
executed do |context|
|
23
|
+
context.fail!("Sorry, something bad has happened.")
|
31
24
|
end
|
25
|
+
end
|
32
26
|
|
33
|
-
|
34
|
-
|
27
|
+
result_context = TestDoubles::MakesCappuccinoAction.execute(
|
28
|
+
:coffee => "espresso",
|
29
|
+
:milk => "2%")
|
35
30
|
|
36
|
-
|
37
|
-
|
38
|
-
end
|
31
|
+
expect(result_context).to be_failure
|
32
|
+
expect(result_context.keys).not_to include(:cappuccino)
|
39
33
|
end
|
34
|
+
end
|
40
35
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end
|
36
|
+
context "when the promised key is in the context" do
|
37
|
+
it "can be set with an actual value" do
|
38
|
+
class TestDoubles::MakesCappuccinoAction
|
39
|
+
executed do |context|
|
40
|
+
context.cappuccino = "#{context.coffee} - with #{context.milk} milk"
|
41
|
+
context.cappuccino += " hot"
|
48
42
|
end
|
49
|
-
|
50
|
-
result_context = DummyActionForKeysToPromise.execute(:tea => "black",
|
51
|
-
:milk => "full cream")
|
52
|
-
expect(result_context).to be_success
|
53
|
-
expect(result_context[:milk_tea]).to eq("black - full cream hello")
|
54
43
|
end
|
55
44
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
context.milk_tea = nil
|
60
|
-
end
|
61
|
-
end
|
62
|
-
result_context = DummyActionForKeysToPromise.execute(:tea => "black",
|
63
|
-
:milk => "full cream")
|
64
|
-
expect(result_context).to be_success
|
65
|
-
expect(result_context[:milk_tea]).to be_nil
|
66
|
-
end
|
67
|
-
end
|
45
|
+
result_context = TestDoubles::MakesCappuccinoAction.execute(
|
46
|
+
:coffee => "espresso",
|
47
|
+
:milk => "2%")
|
68
48
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
expects :coffee
|
73
|
-
promises :cappuccino
|
74
|
-
promises :latte
|
49
|
+
expect(result_context).to be_success
|
50
|
+
expect(result_context.cappuccino).to eq("espresso - with 2% milk hot")
|
51
|
+
end
|
75
52
|
|
53
|
+
it "can be set with nil" do
|
54
|
+
class TestDoubles::MakesCappuccinoAction
|
76
55
|
executed do |context|
|
77
|
-
context.cappuccino =
|
78
|
-
context.latte = "Latte needs #{context.coffee} and a lot of milk"
|
56
|
+
context.cappuccino = nil
|
79
57
|
end
|
80
58
|
end
|
81
|
-
|
59
|
+
result_context = TestDoubles::MakesCappuccinoAction.execute(
|
60
|
+
:coffee => "espresso",
|
61
|
+
:milk => "2%")
|
82
62
|
|
83
|
-
expect(
|
84
|
-
expect(
|
63
|
+
expect(result_context).to be_success
|
64
|
+
expect(result_context[:cappuccino]).to be_nil
|
85
65
|
end
|
66
|
+
end
|
86
67
|
|
68
|
+
it "can collect promised keys when the `promised` macro is called multiple times" do
|
69
|
+
resulting_context = TestDoubles::MultiplePromisesAction.execute(
|
70
|
+
:coffee => "espresso",
|
71
|
+
:milk => "2%")
|
72
|
+
|
73
|
+
expect(resulting_context.cappuccino).to eq("Cappucino needs espresso and a little milk")
|
74
|
+
expect(resulting_context.latte).to eq("Latte needs espresso and a lot of milk")
|
87
75
|
end
|
76
|
+
|
88
77
|
end
|
data/spec/action_spec.rb
CHANGED
@@ -1,84 +1,71 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'test_doubles'
|
2
3
|
|
3
|
-
|
4
|
-
describe Action do
|
5
|
-
class DummyAction
|
6
|
-
include LightService::Action
|
4
|
+
describe LightService::Action do
|
7
5
|
|
8
|
-
|
9
|
-
context[:test_key] = "test_value"
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
class SkippedAction
|
14
|
-
include LightService::Action
|
15
|
-
|
16
|
-
executed do |context|
|
17
|
-
context[:test_key] = "set_by_skipped_action"
|
18
|
-
end
|
19
|
-
end
|
6
|
+
let(:context) { LightService::Context.make }
|
20
7
|
|
21
|
-
|
8
|
+
context "when the action context has failure" do
|
9
|
+
it "returns immediately" do
|
10
|
+
context.fail!("an error")
|
22
11
|
|
23
|
-
|
24
|
-
it "returns immediately" do
|
25
|
-
context.fail!("an error")
|
12
|
+
TestDoubles::AddsTwoAction.execute(context)
|
26
13
|
|
27
|
-
|
28
|
-
|
29
|
-
expect(context.to_hash.keys).to be_empty
|
30
|
-
end
|
14
|
+
expect(context.to_hash.keys).to be_empty
|
31
15
|
end
|
16
|
+
end
|
32
17
|
|
33
|
-
|
34
|
-
|
35
|
-
|
18
|
+
context "when the action context does not have failure" do
|
19
|
+
it "executes the block" do
|
20
|
+
TestDoubles::AddsTwoAction.execute(context)
|
36
21
|
|
37
|
-
|
38
|
-
|
22
|
+
expect(context.to_hash.keys).to eq [:number]
|
23
|
+
expect(context.fetch(:number)).to eq(2)
|
39
24
|
end
|
25
|
+
end
|
40
26
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
DummyAction.execute(context)
|
27
|
+
context "when the action context skips all" do
|
28
|
+
it "returns immediately" do
|
29
|
+
context.skip_all!
|
46
30
|
|
47
|
-
|
48
|
-
end
|
31
|
+
TestDoubles::AddsTwoAction.execute(context)
|
49
32
|
|
50
|
-
|
51
|
-
|
33
|
+
expect(context.to_hash.keys).to be_empty
|
34
|
+
end
|
52
35
|
|
53
|
-
|
36
|
+
it "does not execute skipped actions" do
|
37
|
+
TestDoubles::AddsTwoAction.execute(context)
|
38
|
+
expect(context.to_hash).to eq ({:number => 2})
|
54
39
|
|
55
|
-
|
40
|
+
context.skip_all!
|
56
41
|
|
57
|
-
|
58
|
-
|
42
|
+
TestDoubles::AddsTwoAction.execute(context)
|
43
|
+
# Since the action was skipped, the number remains 2
|
44
|
+
expect(context.to_hash).to eq ({:number => 2})
|
59
45
|
end
|
46
|
+
end
|
60
47
|
|
61
|
-
|
62
|
-
|
48
|
+
it "returns the context" do
|
49
|
+
result = TestDoubles::AddsTwoAction.execute(context)
|
63
50
|
|
64
|
-
|
65
|
-
|
51
|
+
expect(result.to_hash).to eq ({:number => 2})
|
52
|
+
end
|
66
53
|
|
67
|
-
|
68
|
-
|
69
|
-
|
54
|
+
context "when invoked with hash" do
|
55
|
+
it "creates LightService::Context implicitly" do
|
56
|
+
result = TestDoubles::AddsTwoAction.execute(:some_key => "some value")
|
70
57
|
|
71
|
-
|
72
|
-
|
73
|
-
end
|
58
|
+
expect(result).to be_success
|
59
|
+
expect(result.keys).to eq([:some_key, :number])
|
74
60
|
end
|
75
|
-
|
76
|
-
|
77
|
-
|
61
|
+
end
|
62
|
+
|
63
|
+
context "when invoked without arguments" do
|
64
|
+
it "creates LightService::Context implicitly" do
|
65
|
+
result = TestDoubles::AddsTwoAction.execute
|
78
66
|
|
79
|
-
|
80
|
-
|
81
|
-
end
|
67
|
+
expect(result).to be_success
|
68
|
+
expect(result.keys).to eq([:number])
|
82
69
|
end
|
83
70
|
end
|
84
71
|
end
|