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