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