light-service 0.6.0 → 0.6.1
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +34 -0
- data/.travis.yml +16 -2
- data/Appraisals +7 -0
- data/Gemfile +2 -0
- data/README.md +126 -16
- data/RELEASES.md +4 -0
- data/Rakefile +3 -0
- data/gemfiles/activesupport_3.gemfile +8 -0
- data/gemfiles/activesupport_3.gemfile.lock +63 -0
- data/gemfiles/activesupport_4.gemfile +8 -0
- data/gemfiles/activesupport_4.gemfile.lock +71 -0
- data/lib/light-service.rb +1 -1
- data/lib/light-service/action.rb +6 -8
- data/lib/light-service/configuration.rb +0 -2
- data/lib/light-service/context.rb +32 -22
- data/lib/light-service/context/key_verifier.rb +87 -83
- data/lib/light-service/localization_adapter.rb +10 -7
- data/lib/light-service/organizer.rb +6 -3
- data/lib/light-service/organizer/with_reducer.rb +53 -39
- data/lib/light-service/organizer/with_reducer_factory.rb +3 -4
- data/lib/light-service/organizer/with_reducer_log_decorator.rb +81 -51
- data/lib/light-service/version.rb +2 -1
- data/light-service.gemspec +4 -4
- data/resources/fail_actions.png +0 -0
- data/resources/skip_actions.png +0 -0
- data/spec/acceptance/around_each_spec.rb +27 -0
- data/spec/acceptance/include_warning_spec.rb +6 -2
- data/spec/acceptance/log_from_organizer_spec.rb +39 -18
- data/spec/acceptance/message_localization_spec.rb +23 -23
- data/spec/acceptance/rollback_spec.rb +1 -3
- data/spec/action_expected_keys_spec.rb +32 -19
- data/spec/action_promised_keys_spec.rb +72 -54
- data/spec/action_spec.rb +23 -5
- data/spec/context_spec.rb +21 -17
- data/spec/localization_adapter_spec.rb +14 -10
- data/spec/organizer/with_reducer_spec.rb +19 -2
- data/spec/organizer_key_aliases_spec.rb +6 -5
- data/spec/organizer_spec.rb +32 -56
- data/spec/sample/calculates_tax_spec.rb +17 -9
- data/spec/sample/provides_free_shipping_action_spec.rb +3 -7
- data/spec/sample/tax/calculates_order_tax_action.rb +3 -2
- data/spec/sample/tax/calculates_tax.rb +3 -4
- data/spec/sample/tax/looks_up_tax_percentage_action.rb +10 -8
- data/spec/sample/tax/provides_free_shipping_action.rb +2 -4
- data/spec/spec_helper.rb +0 -1
- data/spec/test_doubles.rb +38 -15
- metadata +38 -28
@@ -4,12 +4,12 @@ require "test_doubles"
|
|
4
4
|
class TestsLocalizationAdapter
|
5
5
|
extend LightService::Organizer
|
6
6
|
|
7
|
-
def self.with_message(pass_or_fail, message_or_key, i18n_options={})
|
8
|
-
with(
|
9
|
-
pass_or_fail
|
10
|
-
message_or_key
|
11
|
-
i18n_options
|
12
|
-
|
7
|
+
def self.with_message(pass_or_fail, message_or_key, i18n_options = {})
|
8
|
+
with(
|
9
|
+
:pass_or_fail => pass_or_fail,
|
10
|
+
:message_or_key => message_or_key,
|
11
|
+
:i18n_options => i18n_options
|
12
|
+
).reduce(TestsLocalizationInvocationOptionsAction)
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
@@ -26,31 +26,30 @@ class TestsLocalizationInvocationOptionsAction
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
def pass_with(message_or_key, i18n_options={})
|
29
|
+
def pass_with(message_or_key, i18n_options = {})
|
30
30
|
TestsLocalizationAdapter.with_message(true, message_or_key, i18n_options)
|
31
31
|
end
|
32
32
|
|
33
|
-
def fail_with(message_or_key, i18n_options={})
|
33
|
+
def fail_with(message_or_key, i18n_options = {})
|
34
34
|
TestsLocalizationAdapter.with_message(false, message_or_key, i18n_options)
|
35
35
|
end
|
36
36
|
|
37
37
|
describe "Localization Adapter" do
|
38
|
-
|
39
38
|
before do
|
40
|
-
I18n.backend.store_translations(
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
39
|
+
I18n.backend.store_translations(
|
40
|
+
:en,
|
41
|
+
:tests_localization_invocation_options_action => {
|
42
|
+
:light_service => {
|
43
|
+
:failures => {
|
44
|
+
:some_failure_reason => "This has failed",
|
45
|
+
:failure_with_interpolation => "Failed with %{reason}"
|
46
46
|
},
|
47
|
-
successes
|
48
|
-
some_success_reason
|
49
|
-
success_with_interpolation
|
47
|
+
:successes => {
|
48
|
+
:some_success_reason => "This has passed",
|
49
|
+
:success_with_interpolation => "Passed with %{reason}"
|
50
50
|
}
|
51
51
|
}
|
52
|
-
}
|
53
|
-
})
|
52
|
+
})
|
54
53
|
end
|
55
54
|
|
56
55
|
describe "passing a simple string message" do
|
@@ -96,7 +95,8 @@ describe "Localization Adapter" do
|
|
96
95
|
describe "passing a Symbol with interpolation variables" do
|
97
96
|
describe "by failing the context" do
|
98
97
|
it "performs a translation with interpolation" do
|
99
|
-
result = fail_with(:failure_with_interpolation,
|
98
|
+
result = fail_with(:failure_with_interpolation,
|
99
|
+
:reason => "bad account")
|
100
100
|
|
101
101
|
expect(result).to be_failure
|
102
102
|
expect(result.message).to eq("Failed with bad account")
|
@@ -105,12 +105,12 @@ describe "Localization Adapter" do
|
|
105
105
|
|
106
106
|
describe "by passing the context" do
|
107
107
|
it "performs a translation with interpolation" do
|
108
|
-
result = pass_with(:success_with_interpolation,
|
108
|
+
result = pass_with(:success_with_interpolation,
|
109
|
+
:reason => "account in good standing")
|
109
110
|
|
110
111
|
expect(result).to be_success
|
111
112
|
expect(result.message).to eq("Passed with account in good standing")
|
112
113
|
end
|
113
114
|
end
|
114
115
|
end
|
115
|
-
|
116
116
|
end
|
@@ -2,7 +2,6 @@ require 'spec_helper'
|
|
2
2
|
require 'test_doubles'
|
3
3
|
|
4
4
|
describe ":expects macro" do
|
5
|
-
|
6
5
|
context "when expected keys are in the context" do
|
7
6
|
it "can access the keys as class methods" do
|
8
7
|
resulting_context = TestDoubles::MakesTeaWithMilkAction.execute(
|
@@ -16,35 +15,49 @@ describe ":expects macro" do
|
|
16
15
|
|
17
16
|
context "when an expected key is not in the context" do
|
18
17
|
it "raises an LightService::ExpectedKeysNotInContextError" do
|
19
|
-
|
20
|
-
|
18
|
+
exception_msg = "expected :milk to be in the context during " \
|
19
|
+
"TestDoubles::MakesTeaWithMilkAction"
|
20
|
+
expect do
|
21
21
|
TestDoubles::MakesTeaWithMilkAction.execute(:tea => "black")
|
22
|
-
|
22
|
+
end.to \
|
23
|
+
raise_error(LightService::ExpectedKeysNotInContextError, exception_msg)
|
23
24
|
end
|
24
25
|
end
|
25
26
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
27
|
+
context "when the `expects` macro is called multiple times" do
|
28
|
+
it "can collect expected keys " do
|
29
|
+
result = TestDoubles::MultipleExpectsAction.execute(
|
30
|
+
:tea => "black",
|
31
|
+
:milk => "full cream",
|
32
|
+
:chocolate => "dark chocolate"
|
33
|
+
)
|
34
|
+
expect(result[:milk_tea]).to \
|
35
|
+
eq("black - full cream - with dark chocolate")
|
36
|
+
end
|
33
37
|
end
|
34
38
|
|
35
39
|
context "when a reserved key is listed as an expected key" do
|
36
40
|
it "raises an error indicating a reserved key is expected" do
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
+
exception_msg = "promised or expected keys cannot be a reserved key: " \
|
42
|
+
"[:message]"
|
43
|
+
expect do
|
44
|
+
TestDoubles::MakesTeaExpectingReservedKey.execute(:tea => "black",
|
45
|
+
:message => "no no")
|
46
|
+
end.to \
|
47
|
+
raise_error(LightService::ReservedKeysInContextError, exception_msg)
|
41
48
|
end
|
42
49
|
|
43
50
|
it "raises an error indicating that multiple reserved keys are expected" do
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
51
|
+
exception_msg = "promised or expected keys cannot be a reserved key: " \
|
52
|
+
"[:message, :error_code, :current_action]"
|
53
|
+
expect do
|
54
|
+
TestDoubles::MakesTeaExpectingMultipleReservedKeys
|
55
|
+
.execute(:tea => "black",
|
56
|
+
:message => "no no",
|
57
|
+
:error_code => 1,
|
58
|
+
:current_action => "update")
|
59
|
+
end.to raise_error(LightService::ReservedKeysInContextError,
|
60
|
+
exception_msg)
|
48
61
|
end
|
49
62
|
end
|
50
63
|
end
|
@@ -2,37 +2,43 @@ require 'spec_helper'
|
|
2
2
|
require 'test_doubles'
|
3
3
|
|
4
4
|
describe ":promises macro" do
|
5
|
-
|
6
5
|
context "when the promised key is not in the context" do
|
7
6
|
it "raises an ArgumentError" do
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
module TestDoubles
|
8
|
+
class MakesCappuccinoAction1
|
9
|
+
extend LightService::Action
|
10
|
+
expects :coffee, :milk
|
11
|
+
promises :cappuccino
|
12
|
+
executed do |context|
|
13
|
+
context[:macchiato] = "#{context.coffee} - #{context.milk}"
|
14
|
+
end
|
14
15
|
end
|
15
16
|
end
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
exception_msg = "promised :cappuccino to be in the context during " \
|
19
|
+
"TestDoubles::MakesCappuccinoAction1"
|
20
|
+
expect do
|
21
|
+
TestDoubles::MakesCappuccinoAction1.execute(:coffee => "espresso",
|
22
|
+
:milk => "2%")
|
23
|
+
end.to \
|
24
|
+
raise_error(LightService::PromisedKeysNotInContextError, exception_msg)
|
21
25
|
end
|
22
26
|
|
23
27
|
it "can fail the context without fulfilling its promise" do
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
28
|
+
module TestDoubles
|
29
|
+
class MakesCappuccinoAction2
|
30
|
+
extend LightService::Action
|
31
|
+
expects :coffee, :milk
|
32
|
+
promises :cappuccino
|
33
|
+
executed do |context|
|
34
|
+
context.fail!("Sorry, something bad has happened.")
|
35
|
+
end
|
30
36
|
end
|
31
37
|
end
|
32
38
|
|
33
|
-
result_context = TestDoubles::MakesCappuccinoAction2
|
34
|
-
|
35
|
-
|
39
|
+
result_context = TestDoubles::MakesCappuccinoAction2
|
40
|
+
.execute(:coffee => "espresso",
|
41
|
+
:milk => "2%")
|
36
42
|
|
37
43
|
expect(result_context).to be_failure
|
38
44
|
expect(result_context.keys).not_to include(:cappuccino)
|
@@ -41,36 +47,40 @@ describe ":promises macro" do
|
|
41
47
|
|
42
48
|
context "when the promised key is in the context" do
|
43
49
|
it "can be set with an actual value" do
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
50
|
+
module TestDoubles
|
51
|
+
class MakesCappuccinoAction3
|
52
|
+
extend LightService::Action
|
53
|
+
expects :coffee, :milk
|
54
|
+
promises :cappuccino
|
55
|
+
executed do |context|
|
56
|
+
context.cappuccino = "#{context.coffee} - with #{context.milk} milk"
|
57
|
+
context.cappuccino += " hot"
|
58
|
+
end
|
51
59
|
end
|
52
60
|
end
|
53
61
|
|
54
|
-
result_context = TestDoubles::MakesCappuccinoAction3
|
55
|
-
|
56
|
-
|
62
|
+
result_context = TestDoubles::MakesCappuccinoAction3
|
63
|
+
.execute(:coffee => "espresso",
|
64
|
+
:milk => "2%")
|
57
65
|
|
58
66
|
expect(result_context).to be_success
|
59
67
|
expect(result_context.cappuccino).to eq("espresso - with 2% milk hot")
|
60
68
|
end
|
61
69
|
|
62
70
|
it "can be set with nil" do
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
71
|
+
module TestDoubles
|
72
|
+
class MakesCappuccinoAction4
|
73
|
+
extend LightService::Action
|
74
|
+
expects :coffee, :milk
|
75
|
+
promises :cappuccino
|
76
|
+
executed do |context|
|
77
|
+
context.cappuccino = nil
|
78
|
+
end
|
69
79
|
end
|
70
80
|
end
|
71
|
-
result_context = TestDoubles::MakesCappuccinoAction4
|
72
|
-
|
73
|
-
|
81
|
+
result_context = TestDoubles::MakesCappuccinoAction4
|
82
|
+
.execute(:coffee => "espresso",
|
83
|
+
:milk => "2%")
|
74
84
|
|
75
85
|
expect(result_context).to be_success
|
76
86
|
expect(result_context[:cappuccino]).to be_nil
|
@@ -78,27 +88,35 @@ describe ":promises macro" do
|
|
78
88
|
end
|
79
89
|
|
80
90
|
context "when a reserved key is listed as a promised key" do
|
81
|
-
it "raises
|
82
|
-
|
83
|
-
|
91
|
+
it "raises error indicating a reserved key has been promised" do
|
92
|
+
exception_msg = "promised or expected keys cannot be a reserved key: "\
|
93
|
+
"[:message]"
|
94
|
+
expect do
|
84
95
|
TestDoubles::MakesTeaPromisingReservedKey.execute(:tea => "black")
|
85
|
-
|
96
|
+
end.to \
|
97
|
+
raise_error(LightService::ReservedKeysInContextError, exception_msg)
|
86
98
|
end
|
87
99
|
|
88
|
-
it "raises
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
100
|
+
it "raises error indicating multiple reserved keys have been promised" do
|
101
|
+
exception_msg = "promised or expected keys cannot be a reserved key: " \
|
102
|
+
"[:message, :error_code, :current_action]"
|
103
|
+
expect do
|
104
|
+
ctx = { :tea => "black" }
|
105
|
+
TestDoubles::MakesTeaPromisingMultipleReservedKeys.execute(ctx)
|
106
|
+
end.to \
|
107
|
+
raise_error(LightService::ReservedKeysInContextError, exception_msg)
|
93
108
|
end
|
94
109
|
end
|
95
110
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
111
|
+
context "when the `promised` macro is called multiple times" do
|
112
|
+
it "collects promised keys " do
|
113
|
+
result = TestDoubles::MultiplePromisesAction \
|
114
|
+
.execute(:coffee => "espresso", :milk => "2%")
|
100
115
|
|
101
|
-
|
102
|
-
|
116
|
+
expect(result.cappuccino).to \
|
117
|
+
eq("Cappucino needs espresso and a little milk")
|
118
|
+
expect(result.latte).to \
|
119
|
+
eq("Latte needs espresso and a lot of milk")
|
120
|
+
end
|
103
121
|
end
|
104
122
|
end
|
data/spec/action_spec.rb
CHANGED
@@ -2,7 +2,6 @@ require 'spec_helper'
|
|
2
2
|
require 'test_doubles'
|
3
3
|
|
4
4
|
describe LightService::Action do
|
5
|
-
|
6
5
|
let(:context) { LightService::Context.make }
|
7
6
|
|
8
7
|
context "when the action context has failure" do
|
@@ -13,6 +12,24 @@ describe LightService::Action do
|
|
13
12
|
|
14
13
|
expect(context.to_hash.keys).to be_empty
|
15
14
|
end
|
15
|
+
|
16
|
+
it "returns the failure message in the context" do
|
17
|
+
context.fail!("an error")
|
18
|
+
|
19
|
+
returned_context = TestDoubles::AddsTwoActionWithFetch.execute(context)
|
20
|
+
|
21
|
+
expect(returned_context.message).to eq("an error")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "when the action has an explicit success message" do
|
26
|
+
it "returns the success message in the context" do
|
27
|
+
context.succeed!("successful")
|
28
|
+
|
29
|
+
returned_context = TestDoubles::AddsTwoActionWithFetch.execute(context)
|
30
|
+
|
31
|
+
expect(returned_context.message).to eq("successful")
|
32
|
+
end
|
16
33
|
end
|
17
34
|
|
18
35
|
context "when the action context does not have failure" do
|
@@ -35,25 +52,26 @@ describe LightService::Action do
|
|
35
52
|
|
36
53
|
it "does not execute skipped actions" do
|
37
54
|
TestDoubles::AddsTwoActionWithFetch.execute(context)
|
38
|
-
expect(context.to_hash).to eq
|
55
|
+
expect(context.to_hash).to eq(:number => 2)
|
39
56
|
|
40
57
|
context.skip_all!
|
41
58
|
|
42
59
|
TestDoubles::AddsTwoActionWithFetch.execute(context)
|
43
60
|
# Since the action was skipped, the number remains 2
|
44
|
-
expect(context.to_hash).to eq
|
61
|
+
expect(context.to_hash).to eq(:number => 2)
|
45
62
|
end
|
46
63
|
end
|
47
64
|
|
48
65
|
it "returns the context" do
|
49
66
|
result = TestDoubles::AddsTwoActionWithFetch.execute(context)
|
50
67
|
|
51
|
-
expect(result.to_hash).to eq
|
68
|
+
expect(result.to_hash).to eq(:number => 2)
|
52
69
|
end
|
53
70
|
|
54
71
|
context "when invoked with hash" do
|
55
72
|
it "creates LightService::Context implicitly" do
|
56
|
-
|
73
|
+
ctx = { :some_key => "some value" }
|
74
|
+
result = TestDoubles::AddsTwoActionWithFetch.execute(ctx)
|
57
75
|
|
58
76
|
expect(result).to be_success
|
59
77
|
expect(result.keys).to eq([:some_key, :number])
|
data/spec/context_spec.rb
CHANGED
@@ -2,14 +2,15 @@ require "spec_helper"
|
|
2
2
|
require 'test_doubles'
|
3
3
|
|
4
4
|
describe LightService::Context do
|
5
|
-
|
6
5
|
let(:context) { LightService::Context.make }
|
7
6
|
|
8
7
|
describe "can be made" do
|
9
8
|
context "with no arguments" do
|
10
9
|
subject { LightService::Context.make }
|
11
10
|
it { is_expected.to be_success }
|
12
|
-
|
11
|
+
specify "message is empty string" do
|
12
|
+
expect(context.message).to be_empty
|
13
|
+
end
|
13
14
|
end
|
14
15
|
|
15
16
|
context "with a hash" do
|
@@ -22,7 +23,9 @@ describe LightService::Context do
|
|
22
23
|
|
23
24
|
context "with FAILURE" do
|
24
25
|
it "is failed" do
|
25
|
-
context = LightService::Context.new({},
|
26
|
+
context = LightService::Context.new({},
|
27
|
+
LightService::Outcomes::FAILURE,
|
28
|
+
'')
|
26
29
|
|
27
30
|
expect(context).to be_failure
|
28
31
|
end
|
@@ -31,18 +34,18 @@ describe LightService::Context do
|
|
31
34
|
|
32
35
|
describe "can't be made" do
|
33
36
|
specify "with invalid parameters" do
|
34
|
-
expect{LightService::Context.make([])}.to raise_error(ArgumentError)
|
37
|
+
expect { LightService::Context.make([]) }.to raise_error(ArgumentError)
|
35
38
|
end
|
36
39
|
end
|
37
40
|
|
38
41
|
it "can be asked for success?" do
|
39
|
-
context = LightService::Context.new({},
|
42
|
+
context = LightService::Context.new({}, LightService::Outcomes::SUCCESS)
|
40
43
|
|
41
44
|
expect(context).to be_success
|
42
45
|
end
|
43
46
|
|
44
47
|
it "can be asked for failure?" do
|
45
|
-
context = LightService::Context.new({},
|
48
|
+
context = LightService::Context.new({}, LightService::Outcomes::FAILURE)
|
46
49
|
|
47
50
|
expect(context).to be_failure
|
48
51
|
end
|
@@ -87,19 +90,20 @@ describe LightService::Context do
|
|
87
90
|
expect(context.error_code).to be_nil
|
88
91
|
end
|
89
92
|
|
90
|
-
it "can be pushed into a FAILURE state with an error code in
|
91
|
-
context.fail!("a sad end",
|
93
|
+
it "can be pushed into a FAILURE state with an error code in options hash" do
|
94
|
+
context.fail!("a sad end", 10_005)
|
92
95
|
|
93
96
|
expect(context).to be_failure
|
94
97
|
expect(context.message).to eq("a sad end")
|
95
|
-
expect(context.error_code).to eq(
|
98
|
+
expect(context.error_code).to eq(10_005)
|
96
99
|
end
|
97
100
|
|
98
101
|
it "uses localization adapter to translate failure message" do
|
99
102
|
action_class = TestDoubles::AnAction
|
100
|
-
expect(LightService::Configuration.localization_adapter)
|
101
|
-
|
102
|
-
|
103
|
+
expect(LightService::Configuration.localization_adapter)
|
104
|
+
.to receive(:failure)
|
105
|
+
.with(:failure_reason, action_class, {})
|
106
|
+
.and_return("message")
|
103
107
|
|
104
108
|
context = LightService::Context.make
|
105
109
|
context.current_action = action_class
|
@@ -111,9 +115,10 @@ describe LightService::Context do
|
|
111
115
|
|
112
116
|
it "uses localization adapter to translate success message" do
|
113
117
|
action_class = TestDoubles::AnAction
|
114
|
-
expect(LightService::Configuration.localization_adapter)
|
115
|
-
|
116
|
-
|
118
|
+
expect(LightService::Configuration.localization_adapter)
|
119
|
+
.to receive(:success)
|
120
|
+
.with(:action_passed, action_class, {})
|
121
|
+
.and_return("message")
|
117
122
|
|
118
123
|
context = LightService::Context.make
|
119
124
|
context.current_action = action_class
|
@@ -156,7 +161,7 @@ describe LightService::Context do
|
|
156
161
|
let(:context) do
|
157
162
|
LightService::Context.make(
|
158
163
|
:foo => "foobar",
|
159
|
-
:_aliases => aliases
|
164
|
+
:_aliases => aliases
|
160
165
|
)
|
161
166
|
end
|
162
167
|
let(:aliases) { { :foo => :bar } }
|
@@ -171,5 +176,4 @@ describe LightService::Context do
|
|
171
176
|
expect(context.fetch(:bar)).to eq context[:foo]
|
172
177
|
end
|
173
178
|
end
|
174
|
-
|
175
179
|
end
|