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.
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