light-service 0.9.0 → 0.10.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: 30ef43a9d6e26800b7599546ffea8b1cb15b1533
4
- data.tar.gz: 28ee4863b5ea1a859c9e4ca288c20b23f99bc4e9
3
+ metadata.gz: 24c5a758ab50b3cd0ec404ef0b8a426a3e77e42b
4
+ data.tar.gz: 03ec6d27e900c9685bd141340c2b191df781ab84
5
5
  SHA512:
6
- metadata.gz: dab3019cc90cecb1d97b2d36dcf5ad97dde57a22424eefbe91978d024205ea90215df4045594e8744fc9af5ae58ffd45f9c9727d921aee8af1bae50b0df754d3
7
- data.tar.gz: 528fc079179b6e276aa6b4ec331326b51c279c3e234a0fc5998b336098375ad189c0bc274a616446d9aa851f10cda8653901d7f9e13a2e5ee0251f2f037e9324
6
+ metadata.gz: ef89eee65b07381b629c9f4b8e5aad0e4e719e370e8aa84c32a23f65824d42c3e9c14424810b8518a244df4769b3f2e4f22a1752f6c498e44e146b803041bff3
7
+ data.tar.gz: 3476522df0af4775eff555a986d0dffbfed8dad3c0cd77a7a5eb19ca25b39c7109c8da8756a30625d39bf89e10fb7a54fd6212c15955bc544c37e66f2be6293d
data/.travis.yml CHANGED
@@ -4,10 +4,10 @@ env:
4
4
  - RUN_COVERAGE_REPORT=true
5
5
 
6
6
  rvm:
7
- - 2.1.8
8
7
  - 2.2.2
9
8
  - 2.3.3
10
- - 2.4.0
9
+ - 2.4.1
10
+ - 2.5.0
11
11
 
12
12
  before_install:
13
13
  - 'echo ''gem: --no-ri --no-rdoc'' > ~/.gemrc'
data/README.md CHANGED
@@ -11,7 +11,25 @@
11
11
  ![Orchestrators-Deprecated](resources/orchestrators_deprecated.svg)
12
12
  <br>Version 0.9.0 deprecates Orchestrators and moves all their functionalities into Organizers. Please check out [this PR](https://github.com/adomokos/light-service/pull/132) to see the changes.
13
13
 
14
- <br><br>
14
+ <br>
15
+
16
+ ## Table of Content
17
+ * [Why LightService?](#why-lightservice)
18
+ * [Stopping the Series of Actions](#stopping-the-series-of-actions)
19
+ * [Failing the Context](#failing-the-context)
20
+ * [Skipping the Rest of the Actions](#skipping-the-rest-of-the-actions)
21
+ * [Benchmarking Actions with Around Advice](#benchmarking-actions-with-around-advice)
22
+ * [Before and After Action Hooks](#before-and-after-action-hooks)
23
+ * [Key Aliases](#key-aliases)
24
+ * [Logging](#logging)
25
+ * [Error Codes](#error-codes)
26
+ * [Action Rollback](#action-rollback)
27
+ * [Localizing Messages](#localizing-messages)
28
+ * [Orchestrator Logic in Organizers](#orchestrator-logic-in-organizers)
29
+ * [ContextFactory for Faster Action Testing](#contextfactory-for-faster-action-testing)
30
+
31
+
32
+ ## Why LightService?
15
33
 
16
34
  What do you think of this code?
17
35
 
@@ -116,8 +134,10 @@ class CalculatesOrderTaxAction
116
134
  extend ::LightService::Action
117
135
  expects :order, :tax_percentage
118
136
 
119
- executed do |context|
120
- order.tax = (order.total * (tax_percentage/100)).round(2)
137
+ # I am using ctx as an abbreviation for context
138
+ executed do |ctx|
139
+ order = ctx.order
140
+ order.tax = (order.total * (ctx.tax_percentage/100)).round(2)
121
141
  end
122
142
 
123
143
  end
@@ -126,9 +146,9 @@ class ProvidesFreeShippingAction
126
146
  extend LightService::Action
127
147
  expects :order
128
148
 
129
- executed do |context|
130
- if order.total_with_tax > 200
131
- order.provide_free_shipping!
149
+ executed do |ctx|
150
+ if ctx.order.total_with_tax > 200
151
+ ctx.order.provide_free_shipping!
132
152
  end
133
153
  end
134
154
  end
@@ -156,18 +176,6 @@ I gave a [talk at RailsConf 2013](http://www.adomokos.com/2013/06/simple-and-ele
156
176
  simple and elegant Rails code where I told the story of how LightService was extracted from the projects I had worked on.
157
177
 
158
178
 
159
- ## Table of Content
160
- * [Stopping the Series of Actions](#stopping-the-series-of-actions)
161
- * [Failing the Context](#failing-the-context)
162
- * [Skipping the Rest of the Actions](#skipping-the-rest-of-the-actions)
163
- * [Benchmarking Actions with Around Advice](#benchmarking-actions-with-around-advice)
164
- * [Key Aliases](#key-aliases)
165
- * [Logging](#logging)
166
- * [Error Codes](#error-codes)
167
- * [Action Rollback](#action-rollback)
168
- * [Localizing Messages](#localizing-messages)
169
- * [Orchestrator Logic in Organizers](#orchestrator-logic-in-organizers)
170
- * [ContextFactory for Faster Action Testing](#contextfactory-for-faster-action-testing)
171
179
 
172
180
  ## Stopping the Series of Actions
173
181
  When nothing unexpected happens during the organizer's call, the returned `context` will be successful. Here is how you can check for this:
@@ -196,7 +204,7 @@ When something goes wrong in an action and you want to halt the chain, you need
196
204
  The context's `fail!` method can take an optional message argument, this message might help describing what went wrong.
197
205
  In case you need to return immediately from the point of failure, you have to do that by calling `next context`.
198
206
 
199
- In case you want to fail the context and stop the execution of the executed block, use the `fail_and_return!('something went wront')` method.
207
+ In case you want to fail the context and stop the execution of the executed block, use the `fail_and_return!('something went wrong')` method.
200
208
  This will immediately leave the block, you don't need to call `next context` to return from the block.
201
209
 
202
210
  Here is an example:
@@ -277,6 +285,103 @@ end
277
285
 
278
286
  Any object passed into `around_each` must respond to #call with two arguments: the action name and the context it will execute with. It is also passed a block, where LightService's action execution will be done in, so the result must be returned. While this is a little work, it also gives you before and after state access to the data for any auditing and/or checks you may need to accomplish.
279
287
 
288
+ ## Before and After Action Hooks
289
+
290
+ In case you need to inject code right before and after the actions are executed, you can use the `before_actions` and `after_actions` hooks. It accepts one or multiple lambdas that the Action implementation will invoke. This addition to LightService is a great way to decouple instrumentation from business logic.
291
+
292
+ Consider this code:
293
+
294
+ ```ruby
295
+ class SomeOrganizer
296
+ extend LightService::Organizer
297
+
298
+ def call(ctx)
299
+ with(ctx).reduce(actions)
300
+ end
301
+
302
+ def actions
303
+ OneAction,
304
+ TwoAction,
305
+ ThreeAction
306
+ end
307
+ end
308
+
309
+ class TwoAction
310
+ extend LightService::Action
311
+ expects :user, :logger
312
+
313
+ executed do |ctx|
314
+ # Logging information
315
+ if ctx.user.role == 'admin'
316
+ ctx.logger.info('admin is doing something')
317
+ end
318
+
319
+ ctx.user.do_something
320
+ end
321
+ end
322
+ ```
323
+
324
+ The logging logic makes `TwoAction` more complex, there is more code for logging than for business logic.
325
+
326
+ You have two options to decouple instrumentation from real logic with `before_actions` and `after_actions` hooks:
327
+
328
+ 1. Declare your hooks in the Organizer
329
+ 2. Attach hooks to the Organizer from the outside
330
+
331
+ This is how you can declaratively add before and after hooks to the Organizer:
332
+
333
+ ```ruby
334
+ class SomeOrganizer
335
+ extend LightService::Organizer
336
+ before_actions (lambda do |ctx|
337
+ if ctx.current_action == TwoAction
338
+ return unless ctx.user.role == 'admin'
339
+ ctx.logger.info('admin is doing something')
340
+ end
341
+ end)
342
+ after_actions (lambda do |ctx|
343
+ if ctx.current_action == TwoAction
344
+ return unless ctx.user.role == 'admin'
345
+ ctx.logger.info('admin is DONE doing something')
346
+ end
347
+ end)
348
+
349
+ def call(ctx)
350
+ with(ctx).reduce(actions)
351
+ end
352
+
353
+ def actions
354
+ OneAction,
355
+ TwoAction,
356
+ ThreeAction
357
+ end
358
+ end
359
+
360
+ class TwoAction
361
+ extend LightService::Action
362
+ expects :user
363
+
364
+ executed do |ctx|
365
+ ctx.user.do_something
366
+ end
367
+ end
368
+ ```
369
+
370
+ Note how the action has no logging logic after this change. Also, you can target before and after action logic for specific actions, as the `ctx.current_action` will have the class name of the currently processed action. In the example above, logging will occur only for `TwoAction` and not for `OneAction` or `ThreeAction`.
371
+
372
+ Here is how you can declaratively add `before_hooks` or `after_hooks` to your Organizer from the outside:
373
+
374
+ ```ruby
375
+ SomeOrganizer.before_actions =
376
+ lambda do |ctx|
377
+ if ctx.current_action == TwoAction
378
+ return unless ctx.user.role == 'admin'
379
+ ctx.logger.info('admin is doing something')
380
+ end
381
+ end
382
+ ```
383
+
384
+ These ideas are originally from Aspect Oriented Programming, read more about them [here](https://en.wikipedia.org/wiki/Aspect-oriented_programming).
280
385
 
281
386
  ## Expects and Promises
282
387
  The `expects` and `promises` macros are rules for the inputs/outputs of an action.
@@ -619,7 +724,7 @@ class ExtractsTransformsLoadsData
619
724
  extend LightService::Organizer
620
725
 
621
726
  def self.call(connection)
622
- with(:connection => connection).reduce(steps)
727
+ with(:connection => connection).reduce(actions)
623
728
  end
624
729
 
625
730
  def self.actions
@@ -629,7 +734,7 @@ class ExtractsTransformsLoadsData
629
734
  reduce_if(->(ctx) { ctx.retrieved_items.empty? }, [
630
735
  NotifiesEngineeringTeamAction
631
736
  ]),
632
- iterate(:retrieved_item, [
737
+ iterate(:retrieved_items, [
633
738
  TransformsData
634
739
  ]),
635
740
  LoadsData,
data/RELEASES.md CHANGED
@@ -1,5 +1,8 @@
1
1
  A brief list of new features and changes introduced with the specified version.
2
2
 
3
+ ### 0.10.0
4
+ * Adding [before_actions and after_actions hooks](https://github.com/adomokos/light-service/pull/144).
5
+
3
6
  ### 0.9.0
4
7
  * [Deprecate Orchestrator](https://github.com/adomokos/light-service/pull/132) by moving its functionality to Organizers.
5
8
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "appraisal", "~> 2.0"
6
5
  gem "activesupport", "~> 3.0"
6
+ gem "appraisal", "~> 2.0"
7
7
 
8
8
  gemspec :path => "../"
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "appraisal", "~> 2.0"
6
5
  gem "activesupport", "~> 4.0"
6
+ gem "appraisal", "~> 2.0"
7
7
 
8
8
  gemspec :path => "../"
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "appraisal", "~> 2.0"
6
5
  gem "activesupport", "~> 5.0"
6
+ gem "appraisal", "~> 2.0"
7
7
 
8
8
  gemspec :path => "../"
@@ -23,11 +23,11 @@ module LightService
23
23
  end
24
24
 
25
25
  def expected_keys
26
- @_expected_keys ||= []
26
+ @expected_keys ||= []
27
27
  end
28
28
 
29
29
  def promised_keys
30
- @_promised_keys ||= []
30
+ @promised_keys ||= []
31
31
  end
32
32
 
33
33
  def executed
@@ -42,7 +42,9 @@ module LightService
42
42
  action_context.define_accessor_methods_for_keys(all_keys)
43
43
 
44
44
  catch(:jump_when_failed) do
45
+ call_before_action(action_context)
45
46
  yield(action_context)
47
+ call_after_action(action_context)
46
48
  end
47
49
  end
48
50
  end
@@ -70,6 +72,24 @@ module LightService
70
72
  def all_keys
71
73
  expected_keys + promised_keys
72
74
  end
75
+
76
+ def call_before_action(context)
77
+ invoke_callbacks(context[:_before_actions], context)
78
+ end
79
+
80
+ def call_after_action(context)
81
+ invoke_callbacks(context[:_after_actions], context)
82
+ end
83
+
84
+ def invoke_callbacks(callbacks, context)
85
+ return context unless callbacks
86
+
87
+ callbacks.each do |cb|
88
+ cb.call(context)
89
+ end
90
+
91
+ context
92
+ end
73
93
  end
74
94
  end
75
95
  end
@@ -19,6 +19,17 @@ module LightService
19
19
  def with(data = {})
20
20
  VerifyCallMethodExists.run(self, caller(1..1).first)
21
21
  data[:_aliases] = @aliases if @aliases
22
+
23
+ if @before_actions
24
+ data[:_before_actions] = @before_actions.dup
25
+ @before_actions = nil
26
+ end
27
+
28
+ if @after_actions
29
+ data[:_after_actions] = @after_actions.dup
30
+ @after_actions = nil
31
+ end
32
+
22
33
  WithReducerFactory.make(self).with(data)
23
34
  end
24
35
 
@@ -51,6 +62,22 @@ module LightService
51
62
  def aliases(key_hash)
52
63
  @aliases = key_hash
53
64
  end
65
+
66
+ def before_actions(*logic)
67
+ self.before_actions = logic
68
+ end
69
+
70
+ def before_actions=(logic)
71
+ @before_actions = [logic].flatten
72
+ end
73
+
74
+ def after_actions(*logic)
75
+ self.after_actions = logic
76
+ end
77
+
78
+ def after_actions=(logic)
79
+ @after_actions = [logic].flatten
80
+ end
54
81
  end
55
82
  end
56
83
  end
@@ -1,3 +1,3 @@
1
1
  module LightService
2
- VERSION = "0.9.0".freeze
2
+ VERSION = "0.10.0".freeze
3
3
  end
@@ -19,7 +19,7 @@ Gem::Specification.new do |gem|
19
19
  gem.add_dependency("activesupport", ">= 3.0")
20
20
 
21
21
  gem.add_development_dependency("rspec", "~> 3.0")
22
- gem.add_development_dependency("simplecov", "~> 0.15.1")
23
- gem.add_development_dependency("rubocop", "~> 0.51")
22
+ gem.add_development_dependency("simplecov", "~> 0.16.1")
23
+ gem.add_development_dependency("rubocop", "~> 0.53")
24
24
  gem.add_development_dependency("pry", "~> 0.10")
25
25
  end
@@ -1,10 +1,10 @@
1
1
  require 'spec_helper'
2
2
  require 'test_doubles'
3
3
 
4
- describe TestDoubles::AdditionOrganizer do
5
- it "Adds 1, 2 and 3 to the initial value of 1" do
4
+ RSpec.describe TestDoubles::AdditionOrganizer do
5
+ it 'Adds 1, 2 and 3 to the initial value of 1' do
6
6
  result = TestDoubles::AdditionOrganizer.call(1)
7
- number = result.fetch(:product)
7
+ number = result.fetch(:number)
8
8
 
9
9
  expect(number).to eq(7)
10
10
  end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+ require 'test_doubles'
3
+
4
+ RSpec.describe 'Action after_actions' do
5
+ describe 'works with simple organizers - from outside' do
6
+ it 'can be used to inject code block before each action' do
7
+ TestDoubles::AdditionOrganizer.after_actions =
8
+ lambda do |ctx|
9
+ ctx.number -= 2 if ctx.current_action == TestDoubles::AddsThreeAction
10
+ end
11
+
12
+ result = TestDoubles::AdditionOrganizer.call(0)
13
+
14
+ expect(result.fetch(:number)).to eq(4)
15
+ end
16
+
17
+ it 'works with iterator' do
18
+ TestDoubles::TestIterate.after_actions = [
19
+ lambda do |ctx|
20
+ ctx.number -= 2 if ctx.current_action == TestDoubles::AddsOneAction
21
+ end
22
+ ]
23
+
24
+ result = TestDoubles::TestIterate.call(:number => 0,
25
+ :counters => [1, 2, 3, 4])
26
+
27
+ expect(result).to be_success
28
+ expect(result.number).to eq(-4)
29
+ end
30
+ end
31
+
32
+ describe 'can be added to organizers declaratively' do
33
+ module AfterActions
34
+ class AdditionOrganizer
35
+ extend LightService::Organizer
36
+ after_actions (lambda do |ctx|
37
+ if ctx.current_action == TestDoubles::AddsOneAction
38
+ ctx.number -= 2
39
+ end
40
+ end),
41
+ (lambda do |ctx|
42
+ if ctx.current_action == TestDoubles::AddsThreeAction
43
+ ctx.number -= 3
44
+ end
45
+ end)
46
+
47
+ def self.call(number)
48
+ with(:number => number).reduce(actions)
49
+ end
50
+
51
+ def self.actions
52
+ [
53
+ TestDoubles::AddsOneAction,
54
+ TestDoubles::AddsTwoAction,
55
+ TestDoubles::AddsThreeAction
56
+ ]
57
+ end
58
+ end
59
+ end
60
+
61
+ it 'accepts after_actions hook lambdas from organizer' do
62
+ result = AfterActions::AdditionOrganizer.call(0)
63
+
64
+ expect(result.fetch(:number)).to eq(1)
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,109 @@
1
+ require 'spec_helper'
2
+ require 'test_doubles'
3
+
4
+ RSpec.describe 'Action before_actions' do
5
+ describe 'works with simple organizers - from outside' do
6
+ it 'can be used to inject code block before each action' do
7
+ TestDoubles::AdditionOrganizer.before_actions =
8
+ lambda do |ctx|
9
+ ctx.number -= 2 if ctx.current_action == TestDoubles::AddsThreeAction
10
+ end
11
+
12
+ result = TestDoubles::AdditionOrganizer.call(0)
13
+
14
+ expect(result.fetch(:number)).to eq(4)
15
+ end
16
+
17
+ it 'works with iterator' do
18
+ TestDoubles::TestIterate.before_actions = [
19
+ lambda do |ctx|
20
+ ctx.number -= 2 if ctx.current_action == TestDoubles::AddsOneAction
21
+ end
22
+ ]
23
+
24
+ result = TestDoubles::TestIterate.call(:number => 0,
25
+ :counters => [1, 2, 3, 4])
26
+
27
+ expect(result).to be_success
28
+ expect(result.number).to eq(-4)
29
+ end
30
+ end
31
+
32
+ describe 'can be added to organizers declaratively' do
33
+ module BeforeActions
34
+ class AdditionOrganizer
35
+ extend LightService::Organizer
36
+ before_actions (lambda do |ctx|
37
+ if ctx.current_action == TestDoubles::AddsOneAction
38
+ ctx.number -= 2
39
+ end
40
+ end),
41
+ (lambda do |ctx|
42
+ if ctx.current_action == TestDoubles::AddsThreeAction
43
+ ctx.number -= 3
44
+ end
45
+ end)
46
+
47
+ def self.call(number)
48
+ with(:number => number).reduce(actions)
49
+ end
50
+
51
+ def self.actions
52
+ [
53
+ TestDoubles::AddsOneAction,
54
+ TestDoubles::AddsTwoAction,
55
+ TestDoubles::AddsThreeAction
56
+ ]
57
+ end
58
+ end
59
+ end
60
+
61
+ it 'accepts before_actions hook lambdas from organizer' do
62
+ result = BeforeActions::AdditionOrganizer.call(0)
63
+
64
+ expect(result.fetch(:number)).to eq(1)
65
+ end
66
+ end
67
+
68
+ describe 'works with callbacks' do
69
+ it 'can interact with actions from the outside' do
70
+ TestDoubles::TestWithCallback.before_actions = [
71
+ lambda do |ctx|
72
+ if ctx.current_action == TestDoubles::AddToTotalAction
73
+ ctx.total -= 1000
74
+ end
75
+ end
76
+ ]
77
+ result = TestDoubles::TestWithCallback.call
78
+
79
+ expect(result.counter).to eq(3)
80
+ expect(result.total).to eq(-2994)
81
+ end
82
+ end
83
+
84
+ describe 'can halt all execution with a raised error' do
85
+ it 'does not call the rest of the callback steps' do
86
+ class SkipContextError < StandardError
87
+ attr_reader :ctx
88
+
89
+ def initialize(msg, ctx)
90
+ @ctx = ctx
91
+ super(msg)
92
+ end
93
+ end
94
+ TestDoubles::TestWithCallback.before_actions = [
95
+ lambda do |ctx|
96
+ if ctx.current_action == TestDoubles::IncrementCountAction
97
+ ctx.total -= 1000
98
+ raise SkipContextError.new("stop context now", ctx)
99
+ end
100
+ end
101
+ ]
102
+ begin
103
+ TestDoubles::TestWithCallback.call
104
+ rescue SkipContextError => e
105
+ expect(e.ctx).not_to be_empty
106
+ end
107
+ end
108
+ end
109
+ end
@@ -10,18 +10,18 @@ describe LightService::Orchestrator do
10
10
  with(:number => 1).reduce([
11
11
  TestDoubles::SkipAllAction,
12
12
  reduce_until(->(ctx) { ctx.number == 3 },
13
- TestDoubles::AddOneAction)
13
+ TestDoubles::AddsOneAction)
14
14
  ])
15
15
  end
16
16
 
17
17
  def self.run_skip_after
18
18
  with(:number => 1).reduce([
19
- TestDoubles::AddOneAction,
19
+ TestDoubles::AddsOneAction,
20
20
  reduce_until(->(ctx) { ctx.number == 3 }, [
21
- TestDoubles::AddOneAction
21
+ TestDoubles::AddsOneAction
22
22
  ]),
23
23
  TestDoubles::SkipAllAction,
24
- TestDoubles::AddOneAction
24
+ TestDoubles::AddsOneAction
25
25
  ])
26
26
  end
27
27
 
@@ -29,8 +29,8 @@ describe LightService::Orchestrator do
29
29
  with(:number => 1).reduce([
30
30
  TestDoubles::FailureAction,
31
31
  reduce_until(->(ctx) { ctx[:number] == 3 },
32
- TestDoubles::AddOneAction),
33
- TestDoubles::AddOneAction
32
+ TestDoubles::AddsOneAction),
33
+ TestDoubles::AddsOneAction
34
34
  ])
35
35
  end
36
36
  end
@@ -13,10 +13,10 @@ describe LightService::Orchestrator do
13
13
 
14
14
  def self.steps
15
15
  [
16
- TestDoubles::AddOneAction,
16
+ TestDoubles::AddsOneAction,
17
17
  execute(->(ctx) { ctx.number += 1 }),
18
18
  execute(->(ctx) { ctx[:something] = 'hello' }),
19
- TestDoubles::AddOneAction
19
+ TestDoubles::AddsOneAction
20
20
  ]
21
21
  end
22
22
  end
@@ -10,14 +10,14 @@ describe LightService::Orchestrator do
10
10
  def self.run(context)
11
11
  with(context).reduce([
12
12
  iterate(:numbers, [
13
- TestDoubles::AddOneAction
13
+ TestDoubles::AddsOneAction
14
14
  ])
15
15
  ])
16
16
  end
17
17
 
18
18
  def self.run_single(context)
19
19
  with(context).reduce([
20
- iterate(:numbers, TestDoubles::AddOneAction)
20
+ iterate(:numbers, TestDoubles::AddsOneAction)
21
21
  ])
22
22
  end
23
23
  end
@@ -15,7 +15,7 @@ describe LightService::Orchestrator do
15
15
  it 'responds to both actions and organizers' do
16
16
  result = OrchestratorTestReduce.run({ :number => 0 }, [
17
17
  TestDoubles::AddTwoOrganizer,
18
- TestDoubles::AddOneAction
18
+ TestDoubles::AddsOneAction
19
19
  ])
20
20
 
21
21
  expect(result).to be_success
@@ -26,7 +26,7 @@ describe LightService::Orchestrator do
26
26
  result = OrchestratorTestReduce.run({ :number => 0 }, [
27
27
  TestDoubles::AddTwoOrganizer,
28
28
  TestDoubles::FailureAction,
29
- TestDoubles::AddOneAction
29
+ TestDoubles::AddsOneAction
30
30
  ])
31
31
 
32
32
  expect(result).not_to be_success
@@ -13,9 +13,9 @@ describe LightService::Orchestrator do
13
13
 
14
14
  def self.steps
15
15
  [
16
- TestDoubles::AddOneAction,
16
+ TestDoubles::AddsOneAction,
17
17
  reduce_if(->(ctx) { ctx.number == 1 },
18
- TestDoubles::AddOneAction)
18
+ TestDoubles::AddsOneAction)
19
19
  ]
20
20
  end
21
21
  end
@@ -14,7 +14,7 @@ RSpec.describe LightService::Orchestrator do
14
14
  def self.steps
15
15
  [
16
16
  reduce_until(->(ctx) { ctx.number == 3 },
17
- TestDoubles::AddOneAction)
17
+ TestDoubles::AddsOneAction)
18
18
  ]
19
19
  end
20
20
  end
@@ -13,9 +13,9 @@ RSpec.describe LightService::Organizer do
13
13
 
14
14
  def self.actions
15
15
  [
16
- TestDoubles::AddOneAction,
16
+ TestDoubles::AddsOneAction,
17
17
  reduce_if(->(ctx) { ctx.number == 1 },
18
- TestDoubles::AddOneAction)
18
+ TestDoubles::AddsOneAction)
19
19
  ]
20
20
  end
21
21
  end
@@ -29,11 +29,11 @@ RSpec.describe LightService::Organizer do
29
29
  expect(result.fetch(:number)).to eq(2)
30
30
  expect(result[:logger].logs).to eq(
31
31
  [{
32
- :action => TestDoubles::AddOneAction,
32
+ :action => TestDoubles::AddsOneAction,
33
33
  :before => 0,
34
34
  :after => 1
35
35
  }, {
36
- :action => TestDoubles::AddOneAction,
36
+ :action => TestDoubles::AddsOneAction,
37
37
  :before => 1,
38
38
  :after => 2
39
39
  }]
@@ -9,7 +9,7 @@ RSpec.describe LightService::Organizer do
9
9
  .reduce([
10
10
  TestDoubles::SkipAllAction,
11
11
  reduce_until(->(ctx) { ctx.number == 3 },
12
- TestDoubles::AddOneAction)
12
+ TestDoubles::AddsOneAction)
13
13
  ])
14
14
  end
15
15
  end
@@ -19,12 +19,12 @@ RSpec.describe LightService::Organizer do
19
19
  def self.call
20
20
  with(:number => 1)
21
21
  .reduce([
22
- TestDoubles::AddOneAction,
22
+ TestDoubles::AddsOneAction,
23
23
  reduce_until(->(ctx) { ctx.number == 3 }, [
24
- TestDoubles::AddOneAction
24
+ TestDoubles::AddsOneAction
25
25
  ]),
26
26
  TestDoubles::SkipAllAction,
27
- TestDoubles::AddOneAction
27
+ TestDoubles::AddsOneAction
28
28
  ])
29
29
  end
30
30
  end
@@ -36,8 +36,8 @@ RSpec.describe LightService::Organizer do
36
36
  .reduce([
37
37
  TestDoubles::FailureAction,
38
38
  reduce_until(->(ctx) { ctx[:number] == 3 },
39
- TestDoubles::AddOneAction),
40
- TestDoubles::AddOneAction
39
+ TestDoubles::AddsOneAction),
40
+ TestDoubles::AddsOneAction
41
41
  ])
42
42
  end
43
43
  end
@@ -11,10 +11,10 @@ RSpec.describe LightService::Organizer do
11
11
 
12
12
  def self.steps
13
13
  [
14
- TestDoubles::AddOneAction,
14
+ TestDoubles::AddsOneAction,
15
15
  execute(->(ctx) { ctx.number += 1 }),
16
16
  execute(->(ctx) { ctx[:something] = 'hello' }),
17
- TestDoubles::AddOneAction
17
+ TestDoubles::AddsOneAction
18
18
  ]
19
19
  end
20
20
  end
@@ -2,33 +2,19 @@ require 'spec_helper'
2
2
  require 'test_doubles'
3
3
 
4
4
  RSpec.describe LightService::Organizer do
5
- class TestIterate
6
- extend LightService::Organizer
7
-
8
- def self.call(context)
9
- with(context)
10
- .reduce([iterate(:numbers,
11
- [TestDoubles::AddOneAction])])
12
- end
13
-
14
- def self.call_single(context)
15
- with(context)
16
- .reduce([iterate(:numbers,
17
- TestDoubles::AddOneAction)])
18
- end
19
- end
20
-
21
5
  let(:empty_context) { LightService::Context.make }
22
6
 
23
7
  it 'reduces each item of a collection and singularizes the collection key' do
24
- result = TestIterate.call(:numbers => [1, 2, 3, 4])
8
+ result = TestDoubles::TestIterate.call(:number => 1,
9
+ :counters => [1, 2, 3, 4])
25
10
 
26
11
  expect(result).to be_success
27
12
  expect(result.number).to eq(5)
28
13
  end
29
14
 
30
15
  it 'accepts a single action or organizer' do
31
- result = TestIterate.call_single(:numbers => [1, 2, 3, 4])
16
+ result = TestDoubles::TestIterate.call_single(:number => 1,
17
+ :counters => [1, 2, 3, 4])
32
18
 
33
19
  expect(result).to be_success
34
20
  expect(result.number).to eq(5)
@@ -37,7 +23,7 @@ RSpec.describe LightService::Organizer do
37
23
  it 'will not iterate over a failed context' do
38
24
  empty_context.fail!('Something bad happened')
39
25
 
40
- result = TestIterate.call(empty_context)
26
+ result = TestDoubles::TestIterate.call(empty_context)
41
27
 
42
28
  expect(result).to be_failure
43
29
  end
@@ -45,7 +31,7 @@ RSpec.describe LightService::Organizer do
45
31
  it 'does not iterate over a skipped context' do
46
32
  empty_context.skip_remaining!('No more needed')
47
33
 
48
- result = TestIterate.call(empty_context)
34
+ result = TestDoubles::TestIterate.call(empty_context)
49
35
  expect(result).to be_success
50
36
  end
51
37
  end
@@ -11,9 +11,9 @@ RSpec.describe LightService::Organizer do
11
11
 
12
12
  def self.actions
13
13
  [
14
- TestDoubles::AddOneAction,
14
+ TestDoubles::AddsOneAction,
15
15
  reduce_if(->(ctx) { ctx.number == 1 },
16
- TestDoubles::AddOneAction)
16
+ TestDoubles::AddsOneAction)
17
17
  ]
18
18
  end
19
19
  end
@@ -12,7 +12,7 @@ RSpec.describe LightService::Organizer do
12
12
  def self.actions
13
13
  [
14
14
  reduce_until(->(ctx) { ctx.number == 3 },
15
- TestDoubles::AddOneAction)
15
+ TestDoubles::AddsOneAction)
16
16
  ]
17
17
  end
18
18
  end
@@ -2,68 +2,9 @@ require 'spec_helper'
2
2
  require 'test_doubles'
3
3
 
4
4
  RSpec.describe LightService::Organizer do
5
- class TestWithCallback
6
- extend LightService::Organizer
7
-
8
- def self.call(context = {})
9
- with(context).reduce(actions)
10
- end
11
-
12
- def self.actions
13
- [
14
- SetUpContextAction,
15
- with_callback(IterateCollectionAction,
16
- [IncrementCountAction,
17
- AddToTotalAction])
18
- ]
19
- end
20
- end
21
-
22
- class SetUpContextAction
23
- extend LightService::Action
24
- promises :numbers, :counter, :total
25
-
26
- executed do |ctx|
27
- ctx.numbers = [1, 2, 3]
28
- ctx.counter = 0
29
- ctx.total = 0
30
- end
31
- end
32
-
33
- class IterateCollectionAction
34
- extend LightService::Action
35
- expects :numbers, :callback
36
- promises :number
37
-
38
- executed do |ctx|
39
- ctx.numbers.each do |number|
40
- ctx.number = number
41
- ctx.callback.call(ctx)
42
- end
43
- end
44
- end
45
-
46
- class IncrementCountAction
47
- extend LightService::Action
48
- expects :counter
49
-
50
- executed do |ctx|
51
- ctx.counter = ctx.counter + 1
52
- end
53
- end
54
-
55
- class AddToTotalAction
56
- extend LightService::Action
57
- expects :number, :total
58
-
59
- executed do |ctx|
60
- ctx.total += ctx.number
61
- end
62
- end
63
-
64
5
  describe 'a simple case with a single callback' do
65
6
  it 'calls the actions defined with callback' do
66
- result = TestWithCallback.call
7
+ result = TestDoubles::TestWithCallback.call
67
8
 
68
9
  expect(result.counter).to eq(3)
69
10
  expect(result.total).to eq(6)
@@ -83,9 +24,9 @@ RSpec.describe LightService::Organizer do
83
24
  SetUpNestedContextAction,
84
25
  with_callback(IterateOuterCollectionAction,
85
26
  [IncrementOuterCountAction,
86
- with_callback(IterateCollectionAction,
87
- [IncrementCountAction,
88
- AddToTotalAction])])
27
+ with_callback(TestDoubles::IterateCollectionAction,
28
+ [TestDoubles::IncrementCountAction,
29
+ TestDoubles::AddToTotalAction])])
89
30
  ]
90
31
  end
91
32
  end
@@ -150,9 +91,9 @@ RSpec.describe LightService::Organizer do
150
91
 
151
92
  def self.actions
152
93
  [
153
- SetUpContextAction,
154
- with_callback(IterateCollectionAction,
155
- [IncrementCountAction,
94
+ TestDoubles::SetUpContextAction,
95
+ with_callback(TestDoubles::IterateCollectionAction,
96
+ [TestDoubles::IncrementCountAction,
156
97
  TestDoubles::FailureAction])
157
98
  ]
158
99
  end
@@ -6,10 +6,7 @@ require_relative 'tax/provides_free_shipping_action'
6
6
 
7
7
  describe CalculatesTax do
8
8
  let(:order) { double('order') }
9
- let(:ctx) do
10
- double('ctx', :keys => [:user],
11
- :failure? => false, :skip_remaining? => false)
12
- end
9
+ let(:ctx) { LightService::Context.make(:user => nil) }
13
10
 
14
11
  it "calls the actions in order" do
15
12
  allow(LightService::Context).to receive(:make)
data/spec/test_doubles.rb CHANGED
@@ -30,21 +30,10 @@ module TestDoubles
30
30
  executed(&:fail!)
31
31
  end
32
32
 
33
- class AddOneAction
34
- extend LightService::Action
35
- expects :number
36
- promises :number
37
-
38
- executed do |ctx|
39
- ctx.number += 1
40
- ctx.message = 'Added 1'
41
- end
42
- end
43
-
44
33
  class AddTwoOrganizer
45
34
  extend LightService::Organizer
46
35
  def self.call(context)
47
- with(context).reduce([AddOneAction, AddOneAction])
36
+ with(context).reduce([AddsOneAction, AddsOneAction])
48
37
  end
49
38
  end
50
39
 
@@ -256,10 +245,9 @@ module TestDoubles
256
245
  class AddsThreeAction
257
246
  extend LightService::Action
258
247
  expects :number
259
- promises :product
260
248
 
261
249
  executed do |context|
262
- context.product = context.number + 3
250
+ context.number += 3
263
251
  end
264
252
  end
265
253
 
@@ -341,4 +329,79 @@ module TestDoubles
341
329
 
342
330
  executed { |_ctx| }
343
331
  end
332
+
333
+ class TestIterate
334
+ extend LightService::Organizer
335
+
336
+ def self.call(context)
337
+ with(context)
338
+ .reduce([iterate(:counters,
339
+ [TestDoubles::AddsOneAction])])
340
+ end
341
+
342
+ def self.call_single(context)
343
+ with(context)
344
+ .reduce([iterate(:counters,
345
+ TestDoubles::AddsOneAction)])
346
+ end
347
+ end
348
+
349
+ class TestWithCallback
350
+ extend LightService::Organizer
351
+
352
+ def self.call(context = {})
353
+ with(context).reduce(actions)
354
+ end
355
+
356
+ def self.actions
357
+ [
358
+ SetUpContextAction,
359
+ with_callback(IterateCollectionAction,
360
+ [IncrementCountAction,
361
+ AddToTotalAction])
362
+ ]
363
+ end
364
+ end
365
+
366
+ class SetUpContextAction
367
+ extend LightService::Action
368
+ promises :numbers, :counter, :total
369
+
370
+ executed do |ctx|
371
+ ctx.numbers = [1, 2, 3]
372
+ ctx.counter = 0
373
+ ctx.total = 0
374
+ end
375
+ end
376
+
377
+ class IterateCollectionAction
378
+ extend LightService::Action
379
+ expects :numbers, :callback
380
+ promises :number
381
+
382
+ executed do |ctx|
383
+ ctx.numbers.each do |number|
384
+ ctx.number = number
385
+ ctx.callback.call(ctx)
386
+ end
387
+ end
388
+ end
389
+
390
+ class IncrementCountAction
391
+ extend LightService::Action
392
+ expects :counter
393
+
394
+ executed do |ctx|
395
+ ctx.counter = ctx.counter + 1
396
+ end
397
+ end
398
+
399
+ class AddToTotalAction
400
+ extend LightService::Action
401
+ expects :number, :total
402
+
403
+ executed do |ctx|
404
+ ctx.total += ctx.number
405
+ end
406
+ end
344
407
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: light-service
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Attila Domokos
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-02-03 00:00:00.000000000 Z
11
+ date: 2018-03-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -44,28 +44,28 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 0.15.1
47
+ version: 0.16.1
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 0.15.1
54
+ version: 0.16.1
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rubocop
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0.51'
61
+ version: '0.53'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0.51'
68
+ version: '0.53'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: pry
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -133,7 +133,9 @@ files:
133
133
  - resources/organizer_and_actions.png
134
134
  - resources/skip_actions.png
135
135
  - spec/acceptance/add_numbers_spec.rb
136
+ - spec/acceptance/after_actions_spec.rb
136
137
  - spec/acceptance/around_each_spec.rb
138
+ - spec/acceptance/before_actions_spec.rb
137
139
  - spec/acceptance/fail_spec.rb
138
140
  - spec/acceptance/include_warning_spec.rb
139
141
  - spec/acceptance/log_from_organizer_spec.rb
@@ -204,7 +206,9 @@ specification_version: 4
204
206
  summary: A service skeleton with an emphasis on simplicity
205
207
  test_files:
206
208
  - spec/acceptance/add_numbers_spec.rb
209
+ - spec/acceptance/after_actions_spec.rb
207
210
  - spec/acceptance/around_each_spec.rb
211
+ - spec/acceptance/before_actions_spec.rb
208
212
  - spec/acceptance/fail_spec.rb
209
213
  - spec/acceptance/include_warning_spec.rb
210
214
  - spec/acceptance/log_from_organizer_spec.rb