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.
- 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
@@ -1,50 +1,64 @@
|
|
1
|
-
module LightService
|
2
|
-
|
3
|
-
|
1
|
+
module LightService
|
2
|
+
module Organizer
|
3
|
+
class WithReducer
|
4
|
+
attr_reader :context, :around_each_handler
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
-
|
11
|
+
def around_each(handler)
|
12
|
+
@around_each_handler = handler
|
13
|
+
self
|
25
14
|
end
|
26
|
-
end
|
27
15
|
|
28
|
-
|
29
|
-
|
30
|
-
.
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
-
|
32
|
+
end
|
39
33
|
|
40
|
-
|
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
|
-
|
43
|
-
index_of_current_action = actions.index(@context.current_action) || 0
|
46
|
+
private
|
44
47
|
|
45
|
-
|
46
|
-
|
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
|
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
|
6
|
-
|
5
|
+
if LightService::Configuration.logger.nil?
|
6
|
+
WithReducer.new
|
7
7
|
else
|
8
|
-
|
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
|
2
|
-
|
3
|
-
|
1
|
+
module LightService
|
2
|
+
module Organizer
|
3
|
+
class WithReducerLogDecorator
|
4
|
+
attr_reader :logged, :logger, :decorated, :organizer
|
4
5
|
|
5
|
-
|
6
|
+
alias logged? logged
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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
|
-
|
14
|
-
|
15
|
+
def with(data = {})
|
16
|
+
logger.info("[LightService] - calling organizer <#{organizer}>")
|
15
17
|
|
16
|
-
|
18
|
+
decorated.with(data)
|
17
19
|
|
18
|
-
|
19
|
-
|
20
|
-
|
20
|
+
logger.info("[LightService] - keys in context: " \
|
21
|
+
"#{extract_keys(decorated.context.keys)}")
|
22
|
+
self
|
23
|
+
end
|
21
24
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
-
|
33
|
-
|
34
|
-
|
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
|
-
|
37
|
-
|
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
|
-
|
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
|
-
|
46
|
-
|
47
|
-
|
72
|
+
def extract_keys(keys)
|
73
|
+
keys.map { |key| ":#{key}" }.join(', ')
|
74
|
+
end
|
48
75
|
|
49
|
-
|
50
|
-
|
51
|
-
|
76
|
+
def has_failure?(context)
|
77
|
+
context.respond_to?(:failure?) && context.failure?
|
78
|
+
end
|
52
79
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
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
|
-
|
60
|
-
|
61
|
-
|
86
|
+
def skip_all?(context)
|
87
|
+
context.respond_to?(:skip_all?) && context.skip_all?
|
88
|
+
end
|
62
89
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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
|
99
|
+
end
|
data/light-service.gemspec
CHANGED
@@ -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", ">=
|
19
|
+
gem.add_dependency("activesupport", ">= 3.0")
|
20
20
|
|
21
21
|
gem.add_development_dependency("rspec", "~> 3.0")
|
22
|
-
gem.add_development_dependency("
|
23
|
-
gem.add_development_dependency("
|
24
|
-
gem.add_development_dependency("pry", "0.
|
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
|
Binary file
|
Binary file
|
@@ -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
|
-
|
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
|
-
|
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
|
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
|
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
|
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
|
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] -
|
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:
|
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
|
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
|
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] - :-(((
|
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:
|
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] - :-(((
|
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
|
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] - :-(((
|
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:
|
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] - :-(((
|
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] - ;-)
|
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:
|
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] - ;-)
|
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
|