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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +34 -0
  3. data/.travis.yml +16 -2
  4. data/Appraisals +7 -0
  5. data/Gemfile +2 -0
  6. data/README.md +126 -16
  7. data/RELEASES.md +4 -0
  8. data/Rakefile +3 -0
  9. data/gemfiles/activesupport_3.gemfile +8 -0
  10. data/gemfiles/activesupport_3.gemfile.lock +63 -0
  11. data/gemfiles/activesupport_4.gemfile +8 -0
  12. data/gemfiles/activesupport_4.gemfile.lock +71 -0
  13. data/lib/light-service.rb +1 -1
  14. data/lib/light-service/action.rb +6 -8
  15. data/lib/light-service/configuration.rb +0 -2
  16. data/lib/light-service/context.rb +32 -22
  17. data/lib/light-service/context/key_verifier.rb +87 -83
  18. data/lib/light-service/localization_adapter.rb +10 -7
  19. data/lib/light-service/organizer.rb +6 -3
  20. data/lib/light-service/organizer/with_reducer.rb +53 -39
  21. data/lib/light-service/organizer/with_reducer_factory.rb +3 -4
  22. data/lib/light-service/organizer/with_reducer_log_decorator.rb +81 -51
  23. data/lib/light-service/version.rb +2 -1
  24. data/light-service.gemspec +4 -4
  25. data/resources/fail_actions.png +0 -0
  26. data/resources/skip_actions.png +0 -0
  27. data/spec/acceptance/around_each_spec.rb +27 -0
  28. data/spec/acceptance/include_warning_spec.rb +6 -2
  29. data/spec/acceptance/log_from_organizer_spec.rb +39 -18
  30. data/spec/acceptance/message_localization_spec.rb +23 -23
  31. data/spec/acceptance/rollback_spec.rb +1 -3
  32. data/spec/action_expected_keys_spec.rb +32 -19
  33. data/spec/action_promised_keys_spec.rb +72 -54
  34. data/spec/action_spec.rb +23 -5
  35. data/spec/context_spec.rb +21 -17
  36. data/spec/localization_adapter_spec.rb +14 -10
  37. data/spec/organizer/with_reducer_spec.rb +19 -2
  38. data/spec/organizer_key_aliases_spec.rb +6 -5
  39. data/spec/organizer_spec.rb +32 -56
  40. data/spec/sample/calculates_tax_spec.rb +17 -9
  41. data/spec/sample/provides_free_shipping_action_spec.rb +3 -7
  42. data/spec/sample/tax/calculates_order_tax_action.rb +3 -2
  43. data/spec/sample/tax/calculates_tax.rb +3 -4
  44. data/spec/sample/tax/looks_up_tax_percentage_action.rb +10 -8
  45. data/spec/sample/tax/provides_free_shipping_action.rb +2 -4
  46. data/spec/spec_helper.rb +0 -1
  47. data/spec/test_doubles.rb +38 -15
  48. 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: pass_or_fail,
10
- message_or_key: message_or_key,
11
- i18n_options: i18n_options
12
- }).reduce(TestsLocalizationInvocationOptionsAction)
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(: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}"
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: "This has passed",
49
- success_with_interpolation: "Passed with %{reason}"
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, :reason => "bad account")
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, :reason => "account in good standing")
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
@@ -19,9 +19,7 @@ class AddsOneWithRollbackAction
19
19
  promises :number
20
20
 
21
21
  executed do |context|
22
- if context.number == 0
23
- context.fail_with_rollback!
24
- end
22
+ context.fail_with_rollback! if context.number == 0
25
23
 
26
24
  context.number += 1
27
25
  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
- exception_error_text = "expected :milk to be in the context during TestDoubles::MakesTeaWithMilkAction"
20
- expect {
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
- }.to raise_error(LightService::ExpectedKeysNotInContextError, exception_error_text)
22
+ end.to \
23
+ raise_error(LightService::ExpectedKeysNotInContextError, exception_msg)
23
24
  end
24
25
  end
25
26
 
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")
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
- exception_error_text = "promised or expected keys cannot be a reserved key: [:message]"
38
- expect {
39
- TestDoubles::MakesTeaExpectingReservedKey.execute(:tea => "black", :message => "no no")
40
- }.to raise_error(LightService::ReservedKeysInContextError, exception_error_text)
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
- exception_error_text = "promised or expected keys cannot be a reserved key: [:message, :error_code, :current_action]"
45
- expect {
46
- TestDoubles::MakesTeaExpectingMultipleReservedKeys.execute(:tea => "black", :message => "no no", :error_code => 1, :current_action => "update")
47
- }.to raise_error(LightService::ReservedKeysInContextError, exception_error_text)
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
- class TestDoubles::MakesCappuccinoAction1
9
- extend LightService::Action
10
- expects :coffee, :milk
11
- promises :cappuccino
12
- executed do |context|
13
- context[:macchiato] = "#{context.coffee} - #{context.milk}"
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
- exception_error_text = "promised :cappuccino to be in the context during TestDoubles::MakesCappuccinoAction1"
18
- expect {
19
- TestDoubles::MakesCappuccinoAction1.execute(:coffee => "espresso", :milk => "2%")
20
- }.to raise_error(LightService::PromisedKeysNotInContextError, exception_error_text)
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
- class TestDoubles::MakesCappuccinoAction2
25
- extend LightService::Action
26
- expects :coffee, :milk
27
- promises :cappuccino
28
- executed do |context|
29
- context.fail!("Sorry, something bad has happened.")
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.execute(
34
- :coffee => "espresso",
35
- :milk => "2%")
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
- class TestDoubles::MakesCappuccinoAction3
45
- extend LightService::Action
46
- expects :coffee, :milk
47
- promises :cappuccino
48
- executed do |context|
49
- context.cappuccino = "#{context.coffee} - with #{context.milk} milk"
50
- context.cappuccino += " hot"
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.execute(
55
- :coffee => "espresso",
56
- :milk => "2%")
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
- class TestDoubles::MakesCappuccinoAction4
64
- extend LightService::Action
65
- expects :coffee, :milk
66
- promises :cappuccino
67
- executed do |context|
68
- context.cappuccino = nil
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.execute(
72
- :coffee => "espresso",
73
- :milk => "2%")
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 an error indicating a reserved key has been promised" do
82
- exception_error_text = "promised or expected keys cannot be a reserved key: [:message]"
83
- expect {
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
- }.to raise_error(LightService::ReservedKeysInContextError, exception_error_text)
96
+ end.to \
97
+ raise_error(LightService::ReservedKeysInContextError, exception_msg)
86
98
  end
87
99
 
88
- it "raises an error indicating that multiple reserved keys have been promised" do
89
- exception_error_text = "promised or expected keys cannot be a reserved key: [:message, :error_code, :current_action]"
90
- expect {
91
- TestDoubles::MakesTeaPromisingMultipleReservedKeys.execute(:tea => "black")
92
- }.to raise_error(LightService::ReservedKeysInContextError, exception_error_text)
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
- it "can collect promised keys when the `promised` macro is called multiple times" do
97
- resulting_context = TestDoubles::MultiplePromisesAction.execute(
98
- :coffee => "espresso",
99
- :milk => "2%")
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
- expect(resulting_context.cappuccino).to eq("Cappucino needs espresso and a little milk")
102
- expect(resulting_context.latte).to eq("Latte needs espresso and a lot of milk")
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
@@ -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 ({:number => 2})
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 ({:number => 2})
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 ({:number => 2})
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
- result = TestDoubles::AddsTwoActionWithFetch.execute(:some_key => "some value")
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])
@@ -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
- its(:message) { should be_empty }
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({}, ::LightService::Outcomes::FAILURE, '')
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({}, ::LightService::Outcomes::SUCCESS)
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({}, ::LightService::Outcomes::FAILURE)
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 an options hash" do
91
- context.fail!("a sad end", 10005)
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(10005)
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).to receive(:failure)
101
- .with(:failure_reason, action_class, {})
102
- .and_return("message")
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).to receive(:success)
115
- .with(:action_passed, action_class, {})
116
- .and_return("message")
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