light-service 0.3.6 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fc46d59ad8bc4839942dc2977961ce978bae275f
4
- data.tar.gz: a2ca45e8cb9987ab44dfe5af7080dd45a943efc7
3
+ metadata.gz: 41c85f3b70d95380e2e7aa2c289f089b5415c357
4
+ data.tar.gz: 66e9cb452320935e674a1f1250db139b8a7927ed
5
5
  SHA512:
6
- metadata.gz: 061d0554efd265aa95e83cd7197e24aa12207ea3c4119d297bbd036bc9ed15c5be44af52bcb9b00975b3d0a779f039a1008d6efae6d2ceffa562b65ce9f0cbf0
7
- data.tar.gz: 66fe3f85032dc702b60edcb69cab36a2c6a6bd088f2f04a92a2554aff016d58889092f49baded959e6b161a3bd0490285ce0cc95de54647218cfb6d642ad7087
6
+ metadata.gz: 5783a1a16b678ad68c7352c74a75c506f8b1c3c41e25b43fa77e154f818a04bcff3a6adc641dd3bba779e6fd6cfe319916f4d1c4e3eacd8c06a192705b16a8ce
7
+ data.tar.gz: 0ce7a0c69be92173956db9280aac103654e961913242056c7a7a56264b6b2d22bf3322c764c5ea5d7188a09ebdac0b87de4100e62f0a35ba5508fd37ceffdb8a
data/README.md CHANGED
@@ -200,6 +200,71 @@ end
200
200
 
201
201
  Take a look at [this spec](spec/action_expects_and_promises_spec.rb) to see the refactoring in action.
202
202
 
203
+ ## Logging
204
+
205
+ Enable LightService's logging to better understand what goes on within the series of actions,
206
+ what's in the context or when an action fails.
207
+
208
+ Logging in LightService is turned off by default. However, turning it on is simple. Add this line to your
209
+ project's config file:
210
+
211
+ ```ruby
212
+ LightService::Configuration.logger = Logger.new(STDOUT)
213
+ ```
214
+
215
+ You can turn off the logger by setting it to nil or `/dev/null`.
216
+
217
+ ```ruby
218
+ LightService::Configuration.logger = Logger.new('/dev/null')
219
+ ```
220
+
221
+ In case you're using LightService with Rails, feel free use Rails logger for LightService:
222
+
223
+ ```ruby
224
+ LightService::Configuration.logger = Rails.logger # or config.logger in one of the config files
225
+ ```
226
+
227
+ Watch the console while you are executing the workflow through the organizer. You should see something like this:
228
+
229
+ ```bash
230
+ I, [DATE] INFO -- : [LightService] - calling organizer <TestDoubles::MakesTeaAndCappuccino>
231
+ I, [DATE] INFO -- : [LightService] - keys in context: :tea, :milk, :coffee
232
+ I, [DATE] INFO -- : [LightService] - executing <TestDoubles::MakesTeaWithMilkAction>
233
+ I, [DATE] INFO -- : [LightService] - expects: :tea, :milk
234
+ I, [DATE] INFO -- : [LightService] - promises: :milk_tea
235
+ I, [DATE] INFO -- : [LightService] - keys in context: :tea, :milk, :coffee, :milk_tea
236
+ I, [DATE] INFO -- : [LightService] - executing <TestDoubles::MakesLatteAction>
237
+ I, [DATE] INFO -- : [LightService] - expects: :coffee, :milk
238
+ I, [DATE] INFO -- : [LightService] - promises: :latte
239
+ I, [DATE] INFO -- : [LightService] - keys in context: :tea, :milk, :coffee, :milk_tea, :latte
240
+ ```
241
+
242
+ The log provides a blueprint of the series of actions. You can see what organizer is invoked, what actions
243
+ are called in what order, what do the expect and promise and most importantly what keys you have in the context
244
+ after each action is executed.
245
+
246
+ The logger logs its messages with "INFO" level. The exception to this is the event when an action fails the context.
247
+ That message is logged with "WARN" level:
248
+
249
+ ```bash
250
+ I, [DATE] INFO -- : [LightService] - calling organizer <TestDoubles::MakesCappuccinoAddsTwoAndFails>
251
+ I, [DATE] INFO -- : [LightService] - keys in context: :milk, :coffee
252
+ W, [DATE] WARN -- : [LightService] - :-((( <TestDoubles::MakesLatteAction> has failed...
253
+ W, [DATE] WARN -- : [LightService] - context message: Can't make a latte from a milk that's too hot!
254
+ ```
255
+
256
+ The log message will show you what message was added to the context when the action pushed the
257
+ context into a failure state.
258
+
259
+ The event of skipping the rest of the actions is also captured by its logs:
260
+
261
+ ```bash
262
+ I, [DATE] INFO -- : [LightService] - calling organizer <TestDoubles::MakesCappuccinoSkipsAddsTwo>
263
+ I, [DATE] INFO -- : [LightService] - keys in context: :milk, :coffee
264
+ I, [DATE] INFO -- : [LightService] - ;-) <TestDoubles::MakesLatteAction> has decided to skip the rest of the actions
265
+ I, [DATE] INFO -- : [LightService] - context message: Can't make a latte with a fatty milk like that!
266
+ ```
267
+
203
268
  ## Error Codes
204
269
 
205
270
  You can add some more structure to your error handling by taking advantage of error codes in the context.
@@ -273,40 +338,8 @@ For further examples, please visit the project's [Wiki](https://github.com/adomo
273
338
  Huge thanks to the [contributors](https://github.com/adomokos/light-service/graphs/contributors)!
274
339
 
275
340
  ## Release Notes
276
- ### 0.3.6
277
- * [Collecting](https://github.com/adomokos/light-service/commit/29817de3ad589441788077368ad1d7e723286def) the `expects` and `promises` keys when they are called multiple times in an action
278
-
279
- ### 0.3.5
280
- * remove previously deprecated method Context#context_hash
281
- * [Skipping](https://github.com/adomokos/light-service/commit/d2bd05455a7e4f78aa448db1ea1d692f7b8b67d3) the promised keys check in the context when the context is in failure state
282
-
283
- ### 0.3.4
284
- * The method call `with` is [now optional](https://github.com/adomokos/light-service/blob/master/spec/organizer_spec.rb#L18) in case you have nothing to put into the context.
285
- * Action name is being displayed in the error message when the expected or promised key is not in the context.
286
-
287
- ### 0.3.3
288
- * Switching the promises and expects key accessors from Action to Context
289
-
290
- ### 0.3.2
291
- * Fixing documentation and using separate arguments instead of a hash when setting the context to failure with error code
292
-
293
- ### 0.3.1
294
- * Adding [error codes](https://github.com/adomokos/light-service#error-codes) to the context
295
-
296
- ### 0.3.0
297
- * Adding the `expects` and `promises` macros - Read more about it in [this blog post](http://www.adomokos.com/2014/05/expects-and-promises-in-lightservice.html)
298
-
299
- ### 0.2.2
300
- * Adding the gem version icon to README
301
- * Actions can be invoked now [without arguments](https://github.com/adomokos/light-service/commit/244d5f03b9dbf61c97c1fdb865e6587f9aea177d), this makes it super easy to play with an action in the command line
302
-
303
- ### 0.2.1
304
- * [Improving](https://github.com/adomokos/light-service/commit/fc7043241396b4a2556e9664c13c6929f8330025) deprecation warning for the renamed methods
305
- * Making the message an optional argument for `succeed!` and `fail!` methods
306
341
 
307
- ### 0.2.0
308
- * [Renaming](https://github.com/adomokos/light-service/commit/8d40ff7d393a157a8a558f9e4e021b8731550834) the `set_success!` and `set_failure!` methods to `succeed!` and `fail!`
309
- * [Throwing](https://github.com/adomokos/light-service/commit/5ef315b8aeeafc99e38676adad3c11df5d93b0e3) an ArgumentError if the `make` method's argument is not Hash or LightService::Context
342
+ Follow the release notes in this [document](https://github.com/adomokos/light-service/blob/master/RELEASES.md).
310
343
 
311
344
  ## License
312
345
 
data/RELEASES.md ADDED
@@ -0,0 +1,39 @@
1
+ A brief list of new features and changes introduced with the specified version.
2
+
3
+ ### 0.4.0
4
+ * Adding [logging](https://github.com/adomokos/light-service#logging) to LightService
5
+
6
+ ### 0.3.6
7
+ * [Collecting](https://github.com/adomokos/light-service/commit/29817de3ad589441788077368ad1d7e723286def) the `expects` and `promises` keys when they are called multiple times in an action
8
+
9
+ ### 0.3.5
10
+ * remove previously deprecated method Context#context_hash
11
+ * [Skipping](https://github.com/adomokos/light-service/commit/d2bd05455a7e4f78aa448db1ea1d692f7b8b67d3) the promised keys check in the context when the context is in failure state
12
+
13
+ ### 0.3.4
14
+ * The method call `with` is [now optional](https://github.com/adomokos/light-service/blob/master/spec/organizer_spec.rb#L18) in case you have nothing to put into the context.
15
+ * Action name is being displayed in the error message when the expected or promised key is not in the context.
16
+
17
+ ### 0.3.3
18
+ * Switching the promises and expects key accessors from Action to Context
19
+
20
+ ### 0.3.2
21
+ * Fixing documentation and using separate arguments instead of a hash when setting the context to failure with error code
22
+
23
+ ### 0.3.1
24
+ * Adding [error codes](https://github.com/adomokos/light-service#error-codes) to the context
25
+
26
+ ### 0.3.0
27
+ * Adding the `expects` and `promises` macros - Read more about it in [this blog post](http://www.adomokos.com/2014/05/expects-and-promises-in-lightservice.html)
28
+
29
+ ### 0.2.2
30
+ * Adding the gem version icon to README
31
+ * Actions can be invoked now [without arguments](https://github.com/adomokos/light-service/commit/244d5f03b9dbf61c97c1fdb865e6587f9aea177d), this makes it super easy to play with an action in the command line
32
+
33
+ ### 0.2.1
34
+ * [Improving](https://github.com/adomokos/light-service/commit/fc7043241396b4a2556e9664c13c6929f8330025) deprecation warning for the renamed methods
35
+ * Making the message an optional argument for `succeed!` and `fail!` methods
36
+
37
+ ### 0.2.0
38
+ * [Renaming](https://github.com/adomokos/light-service/commit/8d40ff7d393a157a8a558f9e4e021b8731550834) the `set_success!` and `set_failure!` methods to `succeed!` and `fail!`
39
+ * [Throwing](https://github.com/adomokos/light-service/commit/5ef315b8aeeafc99e38676adad3c11df5d93b0e3) an ArgumentError if the `make` method's argument is not Hash or LightService::Context
data/lib/light-service.rb CHANGED
@@ -1,6 +1,12 @@
1
+ require 'logger'
2
+
1
3
  require "light-service/version"
2
4
 
5
+ require 'light-service/configuration'
3
6
  require 'light-service/context'
4
7
  require 'light-service/context_key_verifier'
8
+ require 'light-service/organizer/with_reducer'
9
+ require 'light-service/organizer/with_reducer_log_decorator'
10
+ require 'light-service/organizer/with_reducer_factory'
5
11
  require 'light-service/action'
6
12
  require 'light-service/organizer'
@@ -0,0 +1,19 @@
1
+ module LightService
2
+ class Configuration
3
+
4
+ class << self
5
+ attr_writer :logger
6
+ end
7
+
8
+ def self.logger
9
+ @logger ||= self._default_logger
10
+ end
11
+
12
+ def self._default_logger
13
+ logger = ::Logger.new("/dev/null")
14
+ logger.level = ::Logger::INFO
15
+ logger
16
+ end
17
+
18
+ end
19
+ end
@@ -7,7 +7,10 @@ module LightService
7
7
  class << self
8
8
  def verify_expected_keys_are_in_context(context, action)
9
9
  verify_keys_are_in_context(context, action.expected_keys) do |not_found_keys|
10
- fail ExpectedKeysNotInContextError, "expected #{format_keys(not_found_keys)} to be in the context during #{action}"
10
+ error_message = "expected #{format_keys(not_found_keys)} to be in the context during #{action}"
11
+
12
+ Configuration.logger.error error_message
13
+ fail ExpectedKeysNotInContextError, error_message
11
14
  end
12
15
  end
13
16
 
@@ -15,7 +18,10 @@ module LightService
15
18
  return context if context.failure?
16
19
 
17
20
  verify_keys_are_in_context(context, action.promised_keys) do |not_found_keys|
18
- fail PromisedKeysNotInContextError, "promised #{format_keys(not_found_keys)} to be in the context during #{action}"
21
+ error_message = "promised #{format_keys(not_found_keys)} to be in the context during #{action}"
22
+
23
+ Configuration.logger.error error_message
24
+ fail PromisedKeysNotInContextError, error_message
19
25
  end
20
26
  end
21
27
 
@@ -4,25 +4,25 @@ module LightService
4
4
  base_class.extend ClassMethods
5
5
  end
6
6
 
7
+ # In case this module is included
7
8
  module ClassMethods
8
9
  def with(data)
9
- new.with(data)
10
+ WithReducerFactory.make(self).with(data)
10
11
  end
11
12
 
12
- def reduce(actions)
13
- new.with.reduce(actions)
13
+ def reduce(*actions)
14
+ WithReducerFactory.make(self).with.reduce(actions)
14
15
  end
15
16
  end
16
17
 
18
+ # Provide hooks for extending the class with these methods
17
19
  def with(data = {})
18
- @context = LightService::Context.make(data)
19
- self
20
+ WithReducerFactory.make(self).with(data)
20
21
  end
21
22
 
22
23
  def reduce(*actions)
23
- raise "No action(s) were provided" if actions.empty?
24
- actions.flatten!
25
- actions.reduce(@context) { |context, action| action.execute(context) }
24
+ WithReducerFactory.make(self).with.reduce(actions)
26
25
  end
26
+
27
27
  end
28
28
  end
@@ -0,0 +1,21 @@
1
+ module LightService; module Organizer
2
+ class WithReducer
3
+ attr_reader :context
4
+
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 |context, action|
15
+ result = action.execute(context)
16
+ yield(context, action) if block_given?
17
+ result
18
+ end
19
+ end
20
+ end
21
+ end; end
@@ -0,0 +1,14 @@
1
+ module LightService
2
+ module Organizer
3
+ class WithReducerFactory
4
+ def self.make(monitored_organizer)
5
+ if (LightService::Configuration.logger.nil?)
6
+ ::LightService::Organizer::WithReducer.new
7
+ else
8
+ ::LightService::Organizer::WithReducerLogDecorator.new(
9
+ ::LightService::Organizer::WithReducer.new, monitored_organizer)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,65 @@
1
+ module LightService; module Organizer
2
+ class WithReducerLogDecorator
3
+ attr_reader :logger, :decorated, :organizer
4
+
5
+ def initialize(decorated = WithReducer.new, organizer)
6
+ @decorated, @organizer = decorated, organizer
7
+ @logger = ::LightService::Configuration.logger
8
+ @logged = false
9
+ end
10
+
11
+ def with(data = {})
12
+ logger.info("[LightService] - calling organizer <#{organizer.to_s}>")
13
+
14
+ decorated.with(data)
15
+
16
+ logger.info("[LightService] - keys in context: #{extract_keys(decorated.context.keys)}")
17
+ self
18
+ end
19
+
20
+ def reduce(*actions)
21
+ decorated.reduce(*actions) do |context, action|
22
+ next if has_failure?(context, action)
23
+ next if skip_all?(context, action)
24
+
25
+ logger.info("[LightService] - executing <#{action.to_s}>")
26
+ if defined? action.expects and action.expects.any?
27
+ logger.info("[LightService] - expects: #{extract_keys(action.expects)}")
28
+ end
29
+ if defined? action.promises and action.promises.any?
30
+ logger.info("[LightService] - promises: #{extract_keys(action.promises)}")
31
+ end
32
+ logger.info("[LightService] - keys in context: #{extract_keys(context.keys)}")
33
+ end
34
+ end
35
+
36
+ private
37
+ def logged?
38
+ @logged
39
+ end
40
+
41
+ def extract_keys(keys)
42
+ keys.map {|key| ":#{key}" }.join(', ')
43
+ end
44
+
45
+ def has_failure?(context, action)
46
+ return false unless context.respond_to?(:failure?)
47
+ return false unless context.failure?
48
+ return true if logged?
49
+
50
+ logger.warn("[LightService] - :-((( <#{action.to_s}> has failed...")
51
+ logger.warn("[LightService] - context message: #{context.message}")
52
+ @logged = true
53
+ end
54
+
55
+ def skip_all?(context, action)
56
+ return false unless context.respond_to?(:skip_all?)
57
+ return false unless context.skip_all?
58
+ return true if logged?
59
+
60
+ logger.info("[LightService] - ;-) <#{action.to_s}> has decided to skip the rest of the actions")
61
+ logger.info("[LightService] - context message: #{context.message}")
62
+ @logged = true
63
+ end
64
+ end
65
+ end; end
@@ -1,3 +1,3 @@
1
1
  module LightService
2
- VERSION = "0.3.6"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
- class Organizer
4
- include LightService::Organizer
3
+ class AdditionOrganizer
4
+ extend LightService::Organizer
5
5
 
6
6
  def self.add_numbers(number)
7
7
  with(:number => number).reduce(
@@ -25,7 +25,6 @@ end
25
25
  class AddsTwoAction
26
26
  include LightService::Action
27
27
  expects :number
28
- promises :number
29
28
 
30
29
  executed do |context|
31
30
  context.number += 2
@@ -35,17 +34,17 @@ end
35
34
  class AddsThreeAction
36
35
  include LightService::Action
37
36
  expects :number
38
- promises :number
37
+ promises :product
39
38
 
40
39
  executed do |context|
41
- context.number += 3
40
+ context.product = context.number + 3
42
41
  end
43
42
  end
44
43
 
45
- describe Organizer do
44
+ describe AdditionOrganizer do
46
45
  it "Adds 1 2 3 and through to 1" do
47
- result = Organizer.add_numbers 1
48
- number = result.fetch(:number)
46
+ result = AdditionOrganizer.add_numbers 1
47
+ number = result.fetch(:product)
49
48
 
50
49
  expect(number).to eq(7)
51
50
  end
@@ -0,0 +1,116 @@
1
+ require 'spec_helper'
2
+ require 'test_doubles'
3
+ require 'stringio'
4
+
5
+ describe "Logs from organizer" do
6
+ def collects_log
7
+ original_logger = LightService::Configuration.logger
8
+
9
+ strio = StringIO.new
10
+ LightService::Configuration.logger = Logger.new(strio)
11
+
12
+ result = yield
13
+
14
+ LightService::Configuration.logger = original_logger
15
+
16
+ strio.string
17
+ end
18
+
19
+ context "when every action has expects or promises" do
20
+ subject(:log_message) do
21
+ collects_log do
22
+ TestDoubles::MakesTeaAndCappuccino.call("black tea", "2% milk", "espresso coffee")
23
+ end
24
+ end
25
+
26
+ it "describes what organizer was invoked" do
27
+ organizer_log_message = "[LightService] - calling organizer <TestDoubles::MakesTeaAndCappuccino>"
28
+ expect(log_message).to include(organizer_log_message)
29
+ end
30
+
31
+ it "describes the actions invoked" do
32
+ organizer_log_message = "[LightService] - executing <TestDoubles::MakesTeaWithMilkAction>"
33
+ expect(log_message).to include(organizer_log_message)
34
+ organizer_log_message = "[LightService] - executing <TestDoubles::MakesLatteAction>"
35
+ expect(log_message).to include(organizer_log_message)
36
+ end
37
+
38
+ it "lists the keys in context before the actions are executed" do
39
+ organizer_log_message = "[LightService] - keys in context: :tea, :milk, :coffee"
40
+ expect(log_message).to include(organizer_log_message)
41
+ end
42
+
43
+ it "lists the expects actions are expecting" do
44
+ organizer_log_message = "[LightService] - expects: :tea, :milk"
45
+ expect(log_message).to include(organizer_log_message)
46
+ organizer_log_message = "[LightService] - expects: :coffee, :milk"
47
+ expect(log_message).to include(organizer_log_message)
48
+ end
49
+
50
+ it "lists the promises actions are promising" do
51
+ organizer_log_message = "[LightService] - promises: :milk_tea"
52
+ expect(log_message).to include(organizer_log_message)
53
+ organizer_log_message = "[LightService] - promises: :latte"
54
+ expect(log_message).to include(organizer_log_message)
55
+ end
56
+
57
+ 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"
59
+ expect(log_message).to include(organizer_log_message)
60
+ end
61
+ end
62
+
63
+ context "when NOT every action expects or promises" do
64
+ subject(:log_message) do
65
+ collects_log do
66
+ TestDoubles::MakesCappuccinoAddsTwo.call("2% milk", "espresso coffee")
67
+ end
68
+ end
69
+
70
+ it "describes what organizer was invoked" do
71
+ organizer_log_message = "[LightService] - calling organizer <TestDoubles::MakesCappuccinoAddsTwo>"
72
+ expect(log_message).to include(organizer_log_message)
73
+ end
74
+
75
+ it "does not list empty expects or promises" do
76
+ organizer_log_message = "[LightService] - expects:\n"
77
+ expect(log_message).not_to include(organizer_log_message)
78
+ organizer_log_message = "[LightService] - promises:\n"
79
+ expect(log_message).not_to include(organizer_log_message)
80
+ end
81
+ end
82
+
83
+ context "when the context has failed" do
84
+ subject(:log_message) do
85
+ collects_log do
86
+ TestDoubles::MakesCappuccinoAddsTwoAndFails.call("espresso coffee")
87
+ end
88
+ end
89
+
90
+ it "logs it with a warning" do
91
+ organizer_log_message = "WARN -- : [LightService] - :-((( <TestDoubles::MakesLatteAction> has failed..."
92
+ 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 too hot!"
94
+ expect(log_message).to include(organizer_log_message)
95
+ organizer_log_message = "[LightService] - :-((( <TestDoubles::AddsTwoAction> has failed..."
96
+ expect(log_message).not_to include(organizer_log_message)
97
+ end
98
+ end
99
+
100
+ context "when the context is skipping the rest" do
101
+ subject(:log_message) do
102
+ collects_log do
103
+ TestDoubles::MakesCappuccinoSkipsAddsTwo.call("espresso coffee")
104
+ end
105
+ end
106
+
107
+ it "logs it with a warning" do
108
+ organizer_log_message = "INFO -- : [LightService] - ;-) <TestDoubles::MakesLatteAction> has decided to skip the rest of the actions"
109
+ expect(log_message).to include(organizer_log_message)
110
+ organizer_log_message = "INFO -- : [LightService] - context message: Can't make a latte with a fatty milk like that!"
111
+ expect(log_message).to include(organizer_log_message)
112
+ organizer_log_message = "INFO -- : [LightService] - ;-) <TestDoubles::AddsTwoAction> has decided to skip the rest of the actions"
113
+ expect(log_message).not_to include(organizer_log_message)
114
+ end
115
+ end
116
+ end