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
@@ -1,50 +1,64 @@
1
- module LightService; module Organizer
2
- class WithReducer
3
- attr_reader :context
1
+ module LightService
2
+ module Organizer
3
+ class WithReducer
4
+ attr_reader :context, :around_each_handler
4
5
 
5
- def with(data = {})
6
- @context = LightService::Context.make(data)
7
- self
8
- end
9
-
10
- def reduce(*actions)
11
- raise "No action(s) were provided" if actions.empty?
12
- actions.flatten!
13
-
14
- actions.reduce(context) do |current_context, action|
15
- begin
16
- result = action.execute(current_context)
17
- rescue FailWithRollbackError
18
- result = reduce_rollback(actions)
19
- ensure
20
- # For logging
21
- yield(current_context, action) if block_given?
22
- end
6
+ def with(data = {})
7
+ @context = LightService::Context.make(data)
8
+ self
9
+ end
23
10
 
24
- result
11
+ def around_each(handler)
12
+ @around_each_handler = handler
13
+ self
25
14
  end
26
- end
27
15
 
28
- def reduce_rollback(actions)
29
- reversable_actions(actions)
30
- .reverse
31
- .reduce(context) do |context, action|
32
- if action.respond_to?(:rollback)
33
- action.rollback(context)
34
- else
35
- context
16
+ def reduce(*actions)
17
+ fail "No action(s) were provided" if actions.empty?
18
+ actions.flatten!
19
+
20
+ actions.reduce(context) do |current_context, action|
21
+ begin
22
+ result = invoke_action(current_context, action)
23
+ rescue FailWithRollbackError
24
+ result = reduce_rollback(actions)
25
+ ensure
26
+ # For logging
27
+ yield(current_context, action) if block_given?
36
28
  end
29
+
30
+ result
37
31
  end
38
- end
32
+ end
39
33
 
40
- private
34
+ def reduce_rollback(actions)
35
+ reversable_actions(actions)
36
+ .reverse
37
+ .reduce(context) do |context, action|
38
+ if action.respond_to?(:rollback)
39
+ action.rollback(context)
40
+ else
41
+ context
42
+ end
43
+ end
44
+ end
41
45
 
42
- def reversable_actions(actions)
43
- index_of_current_action = actions.index(@context.current_action) || 0
46
+ private
44
47
 
45
- # Reverse from the point where the fail was triggered
46
- actions.take(index_of_current_action + 1)
47
- end
48
+ def invoke_action(current_context, action)
49
+ return action.execute(current_context) unless around_each_handler
48
50
 
51
+ around_each_handler.call(action, current_context) do
52
+ action.execute(current_context)
53
+ end
54
+ end
55
+
56
+ def reversable_actions(actions)
57
+ index_of_current_action = actions.index(@context.current_action) || 0
58
+
59
+ # Reverse from the point where the fail was triggered
60
+ actions.take(index_of_current_action + 1)
61
+ end
62
+ end
49
63
  end
50
- end; end
64
+ end
@@ -2,11 +2,10 @@ module LightService
2
2
  module Organizer
3
3
  class WithReducerFactory
4
4
  def self.make(monitored_organizer)
5
- if (LightService::Configuration.logger.nil?)
6
- ::LightService::Organizer::WithReducer.new
5
+ if LightService::Configuration.logger.nil?
6
+ WithReducer.new
7
7
  else
8
- ::LightService::Organizer::WithReducerLogDecorator.new(
9
- ::LightService::Organizer::WithReducer.new, monitored_organizer)
8
+ WithReducerLogDecorator.new(monitored_organizer, WithReducer.new)
10
9
  end
11
10
  end
12
11
  end
@@ -1,69 +1,99 @@
1
- module LightService; module Organizer
2
- class WithReducerLogDecorator
3
- attr_reader :logged, :logger, :decorated, :organizer
1
+ module LightService
2
+ module Organizer
3
+ class WithReducerLogDecorator
4
+ attr_reader :logged, :logger, :decorated, :organizer
4
5
 
5
- alias_method :logged?, :logged
6
+ alias logged? logged
6
7
 
7
- def initialize(decorated = WithReducer.new, organizer)
8
- @decorated, @organizer = decorated, organizer
9
- @logger = ::LightService::Configuration.logger
10
- @logged = false
11
- end
8
+ def initialize(organizer, decorated = WithReducer.new)
9
+ @decorated = decorated
10
+ @organizer = organizer
11
+ @logger = LightService::Configuration.logger
12
+ @logged = false
13
+ end
12
14
 
13
- def with(data = {})
14
- logger.info("[LightService] - calling organizer <#{organizer.to_s}>")
15
+ def with(data = {})
16
+ logger.info("[LightService] - calling organizer <#{organizer}>")
15
17
 
16
- decorated.with(data)
18
+ decorated.with(data)
17
19
 
18
- logger.info("[LightService] - keys in context: #{extract_keys(decorated.context.keys)}")
19
- self
20
- end
20
+ logger.info("[LightService] - keys in context: " \
21
+ "#{extract_keys(decorated.context.keys)}")
22
+ self
23
+ end
21
24
 
22
- def reduce(*actions)
23
- decorated.reduce(*actions) do |context, action|
24
- next if logged?
25
- if has_failure?(context)
26
- write_failure_log(context, action) and next
27
- end
28
- if skip_all?(context)
29
- write_skip_all_log(context, action) and next
30
- end
25
+ def around_each(handler)
26
+ decorated.around_each(handler)
27
+ self
28
+ end
31
29
 
32
- logger.info("[LightService] - executing <#{action.to_s}>")
33
- if defined? action.expects and action.expects.any?
34
- logger.info("[LightService] - expects: #{extract_keys(action.expects)}")
30
+ def reduce(*actions)
31
+ decorated.reduce(*actions) do |context, action|
32
+ next context if logged?
33
+
34
+ if has_failure?(context)
35
+ write_failure_log(context, action)
36
+ next context
37
+ end
38
+
39
+ if skip_all?(context)
40
+ write_skip_all_log(context, action)
41
+ next context
42
+ end
43
+
44
+ write_log(action, context)
35
45
  end
36
- if defined? action.promises and action.promises.any?
37
- logger.info("[LightService] - promises: #{extract_keys(action.promises)}")
46
+ end
47
+
48
+ private
49
+
50
+ def write_log(action, context)
51
+ logger.info("[LightService] - executing <#{action}>")
52
+ log_expects(action)
53
+ log_promises(action)
54
+ logger.info("[LightService] - keys in context: "\
55
+ "#{extract_keys(context.keys)}")
56
+ end
57
+
58
+ def log_expects(action)
59
+ if defined?(action.expects) && action.expects.any?
60
+ logger.info("[LightService] - expects: " \
61
+ "#{extract_keys(action.expects)}")
38
62
  end
39
- logger.info("[LightService] - keys in context: #{extract_keys(context.keys)}")
40
63
  end
41
- end
42
64
 
43
- private
65
+ def log_promises(action)
66
+ if defined?(action.promises) && action.promises.any?
67
+ logger.info("[LightService] - promises: " \
68
+ "#{extract_keys(action.promises)}")
69
+ end
70
+ end
44
71
 
45
- def extract_keys(keys)
46
- keys.map {|key| ":#{key}" }.join(', ')
47
- end
72
+ def extract_keys(keys)
73
+ keys.map { |key| ":#{key}" }.join(', ')
74
+ end
48
75
 
49
- def has_failure?(context)
50
- context.respond_to?(:failure?) && context.failure?
51
- end
76
+ def has_failure?(context)
77
+ context.respond_to?(:failure?) && context.failure?
78
+ end
52
79
 
53
- def write_failure_log(context, action)
54
- logger.warn("[LightService] - :-((( <#{action.to_s}> has failed...")
55
- logger.warn("[LightService] - context message: #{context.message}")
56
- @logged = true
57
- end
80
+ def write_failure_log(context, action)
81
+ logger.warn("[LightService] - :-((( <#{action}> has failed...")
82
+ logger.warn("[LightService] - context message: #{context.message}")
83
+ @logged = true
84
+ end
58
85
 
59
- def skip_all?(context)
60
- context.respond_to?(:skip_all?) && context.skip_all?
61
- end
86
+ def skip_all?(context)
87
+ context.respond_to?(:skip_all?) && context.skip_all?
88
+ end
62
89
 
63
- def write_skip_all_log(context, action)
64
- logger.info("[LightService] - ;-) <#{action.to_s}> has decided to skip the rest of the actions")
65
- logger.info("[LightService] - context message: #{context.message}")
66
- @logged = true
90
+ def write_skip_all_log(context, action)
91
+ msg = "[LightService] - ;-) <#{action}> has decided " \
92
+ "to skip the rest of the actions"
93
+ logger.info(msg)
94
+ logger.info("[LightService] - context message: #{context.message}")
95
+ @logged = true
96
+ end
67
97
  end
68
98
  end
69
- end; end
99
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module LightService
2
- VERSION = "0.6.0"
3
+ VERSION = "0.6.1".freeze
3
4
  end
@@ -16,10 +16,10 @@ Gem::Specification.new do |gem|
16
16
  gem.require_paths = ["lib"]
17
17
  gem.version = LightService::VERSION
18
18
 
19
- gem.add_dependency("activesupport", ">= 4.0")
19
+ gem.add_dependency("activesupport", ">= 3.0")
20
20
 
21
21
  gem.add_development_dependency("rspec", "~> 3.0")
22
- gem.add_development_dependency("rspec-its", "~> 1.0")
23
- gem.add_development_dependency("simplecov", "~> 0.7.1")
24
- gem.add_development_dependency("pry", "0.9.12.2")
22
+ gem.add_development_dependency("simplecov", "~> 0.11")
23
+ gem.add_development_dependency("rubocop", "~> 0.36")
24
+ gem.add_development_dependency("pry", "~> 0.10")
25
25
  end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+ require 'test_doubles'
3
+
4
+ describe "Executing arbitrary code around each action" do
5
+ def assert_before_action_execute_log
6
+ expect(MyLogger).to receive(:info)
7
+ .with(TestDoubles::AddsTwoActionWithFetch, :number => 0)
8
+ end
9
+
10
+ def assert_after_action_execute_log
11
+ expect(MyLogger).to receive(:info)
12
+ .with(TestDoubles::AddsTwoActionWithFetch, :number => 2)
13
+ end
14
+
15
+ it "can be used to log data" do
16
+ MyLogger = double
17
+ context = { :number => 0 }
18
+
19
+ assert_before_action_execute_log
20
+ assert_after_action_execute_log
21
+
22
+ result = TestDoubles::AroundEachOrganizer.add(context)
23
+
24
+ expect(result.fetch(:number)).to eq(2)
25
+ end
26
+ end
27
+
@@ -4,8 +4,10 @@ require 'test_doubles'
4
4
  describe "Including is discouraged" do
5
5
  context "when including LightService::Organizer" do
6
6
  it "gives warning" do
7
+ expected_msg = "including LightService::Organizer is deprecated. " \
8
+ "Please use `extend LightService::Organizer` instead"
7
9
  expect(ActiveSupport::Deprecation).to receive(:warn)
8
- .with("including LightService::Organizer is deprecated. Please use `extend LightService::Organizer` instead")
10
+ .with(expected_msg)
9
11
 
10
12
  class OrganizerIncludingLS
11
13
  include LightService::Organizer
@@ -15,8 +17,10 @@ describe "Including is discouraged" do
15
17
 
16
18
  context "when including LightService::Action" do
17
19
  it "gives warning" do
20
+ expected_msg = "including LightService::Action is deprecated. " \
21
+ "Please use `extend LightService::Action` instead"
18
22
  expect(ActiveSupport::Deprecation).to receive(:warn)
19
- .with("including LightService::Action is deprecated. Please use `extend LightService::Action` instead")
23
+ .with(expected_msg)
20
24
 
21
25
  class ActionIncludingLS
22
26
  include LightService::Action
@@ -19,24 +19,29 @@ describe "Logs from organizer" do
19
19
  context "when every action has expects or promises" do
20
20
  subject(:log_message) do
21
21
  collects_log do
22
- TestDoubles::MakesTeaAndCappuccino.call("black tea", "2% milk", "espresso coffee")
22
+ TestDoubles::MakesTeaAndCappuccino
23
+ .call("black tea", "2% milk", "espresso coffee")
23
24
  end
24
25
  end
25
26
 
26
27
  it "describes what organizer was invoked" do
27
- organizer_log_message = "[LightService] - calling organizer <TestDoubles::MakesTeaAndCappuccino>"
28
+ organizer_log_message = "[LightService] - calling organizer " \
29
+ "<TestDoubles::MakesTeaAndCappuccino>"
28
30
  expect(log_message).to include(organizer_log_message)
29
31
  end
30
32
 
31
33
  it "describes the actions invoked" do
32
- organizer_log_message = "[LightService] - executing <TestDoubles::MakesTeaWithMilkAction>"
34
+ organizer_log_message = "[LightService] - executing " \
35
+ "<TestDoubles::MakesTeaWithMilkAction>"
33
36
  expect(log_message).to include(organizer_log_message)
34
- organizer_log_message = "[LightService] - executing <TestDoubles::MakesLatteAction>"
37
+ organizer_log_message = "[LightService] - executing " \
38
+ "<TestDoubles::MakesLatteAction>"
35
39
  expect(log_message).to include(organizer_log_message)
36
40
  end
37
41
 
38
42
  it "lists the keys in context before the actions are executed" do
39
- organizer_log_message = "[LightService] - keys in context: :tea, :milk, :coffee"
43
+ organizer_log_message = "[LightService] - " \
44
+ "keys in context: :tea, :milk, :coffee"
40
45
  expect(log_message).to include(organizer_log_message)
41
46
  end
42
47
 
@@ -55,7 +60,8 @@ describe "Logs from organizer" do
55
60
  end
56
61
 
57
62
  it "lists the keys in contect after the actions are executed" do
58
- organizer_log_message = "[LightService] - keys in context: :tea, :milk, :coffee, :milk_tea, :latte"
63
+ organizer_log_message = "[LightService] - keys in context: " \
64
+ ":tea, :milk, :coffee, :milk_tea, :latte"
59
65
  expect(log_message).to include(organizer_log_message)
60
66
  end
61
67
  end
@@ -68,7 +74,8 @@ describe "Logs from organizer" do
68
74
  end
69
75
 
70
76
  it "describes what organizer was invoked" do
71
- organizer_log_message = "[LightService] - calling organizer <TestDoubles::MakesCappuccinoAddsTwo>"
77
+ organizer_log_message = "[LightService] - calling organizer " \
78
+ "<TestDoubles::MakesCappuccinoAddsTwo>"
72
79
  expect(log_message).to include(organizer_log_message)
73
80
  end
74
81
 
@@ -83,16 +90,20 @@ describe "Logs from organizer" do
83
90
  context "when the context has failed" do
84
91
  subject(:log_message) do
85
92
  collects_log do
86
- TestDoubles::MakesCappuccinoAddsTwoAndFails.call("espresso coffee")
93
+ TestDoubles::MakesCappuccinoAddsTwoAndFails
94
+ .call("espresso coffee")
87
95
  end
88
96
  end
89
97
 
90
98
  it "logs it with a warning" do
91
- organizer_log_message = "WARN -- : [LightService] - :-((( <TestDoubles::MakesLatteAction> has failed..."
99
+ organizer_log_message = "WARN -- : [LightService] - :-((( " \
100
+ "<TestDoubles::MakesLatteAction> has failed..."
92
101
  expect(log_message).to include(organizer_log_message)
93
- organizer_log_message = "WARN -- : [LightService] - context message: Can't make a latte from a milk that's very hot!"
102
+ organizer_log_message = "WARN -- : [LightService] - context message: " \
103
+ "Can't make a latte from a milk that's very hot!"
94
104
  expect(log_message).to include(organizer_log_message)
95
- organizer_log_message = "[LightService] - :-((( <TestDoubles::AddsTwoAction> has failed..."
105
+ organizer_log_message = "[LightService] - :-((( " \
106
+ "<TestDoubles::AddsTwoAction> has failed..."
96
107
  expect(log_message).not_to include(organizer_log_message)
97
108
  end
98
109
  end
@@ -100,16 +111,21 @@ describe "Logs from organizer" do
100
111
  context "when the context has failed with rollback" do
101
112
  subject(:log_message) do
102
113
  collects_log do
103
- TestDoubles::MakesCappuccinoAddsTwoAndFails.call("espresso coffee", :super_hot)
114
+ TestDoubles::MakesCappuccinoAddsTwoAndFails
115
+ .call("espresso coffee", :super_hot)
104
116
  end
105
117
  end
106
118
 
107
119
  it "logs it with a warning" do
108
- organizer_log_message = "WARN -- : [LightService] - :-((( <TestDoubles::MakesLatteAction> has failed..."
120
+ organizer_log_message = "WARN -- : [LightService] - :-((( " \
121
+ "<TestDoubles::MakesLatteAction> has failed..."
109
122
  expect(log_message).to include(organizer_log_message)
110
- organizer_log_message = "WARN -- : [LightService] - context message: Can't make a latte from a milk that's super hot!"
123
+ organizer_log_message = "WARN -- : [LightService] - context message: " \
124
+ "Can't make a latte from a milk that's super hot!"
111
125
  expect(log_message).to include(organizer_log_message)
112
- organizer_log_message = "[LightService] - :-((( <TestDoubles::AddsTwoAction> has failed..."
126
+ organizer_log_message = "[LightService] - :-((( " \
127
+ "<TestDoubles::AddsTwoAction> " \
128
+ "has failed..."
113
129
  expect(log_message).not_to include(organizer_log_message)
114
130
  end
115
131
  end
@@ -122,11 +138,16 @@ describe "Logs from organizer" do
122
138
  end
123
139
 
124
140
  it "logs it with a warning" do
125
- organizer_log_message = "INFO -- : [LightService] - ;-) <TestDoubles::MakesLatteAction> has decided to skip the rest of the actions"
141
+ organizer_log_message = "INFO -- : [LightService] - ;-) " \
142
+ "<TestDoubles::MakesLatteAction> has decided " \
143
+ "to skip the rest of the actions"
126
144
  expect(log_message).to include(organizer_log_message)
127
- organizer_log_message = "INFO -- : [LightService] - context message: Can't make a latte with a fatty milk like that!"
145
+ organizer_log_message = "INFO -- : [LightService] - context message: " \
146
+ "Can't make a latte with a fatty milk like that!"
128
147
  expect(log_message).to include(organizer_log_message)
129
- organizer_log_message = "INFO -- : [LightService] - ;-) <TestDoubles::AddsTwoAction> has decided to skip the rest of the actions"
148
+ organizer_log_message = "INFO -- : [LightService] - ;-) " \
149
+ "<TestDoubles::AddsTwoAction> has decided " \
150
+ "to skip the rest of the actions"
130
151
  expect(log_message).not_to include(organizer_log_message)
131
152
  end
132
153
  end