light-service 0.7.0 → 0.8.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: 20a9e0cfd0b42cb8c0fb35b808f099c50a7ca32f
4
- data.tar.gz: d8d76249eafe1e4cdeb1830b8b13453905941b7a
3
+ metadata.gz: f66d801de921ff601005501d2aec87d3943138fc
4
+ data.tar.gz: 6c9ea24dac7c7500e05d5b99ca5898bcfe97fa7f
5
5
  SHA512:
6
- metadata.gz: d117cd24d38771a808e1210877387d167a74ad9588c314e34db5903c7b86ded6fdf1760a8a11467051626ea286c017729f82b5f4fe70198d125973b8eb0cc011
7
- data.tar.gz: e6aaf6e9b0605ad347ad290ef35c2fb9f9afb5a47676b3be01473dd9b57c54ca337c8a5ea5add5541363fecb1c06a187f93a156bb39bc5d72a5082b33172715b
6
+ metadata.gz: 5607646dacdc11ac1201ed7a6b5be6f963a43b3838263edc1909db4a032e92a051f3555ec71ea3006084d211c9b2dd8f12fc755ff9fc0889689ef9a9a7c68152
7
+ data.tar.gz: 0f42791e151e88a49a0018705c78ea1e1471df2f57f13c2fad886a25a28cc4bfa9126618a1aeee5df2760d0574a29655dd926d84d29edff4a5bab742b4993970
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- ![LightService](https://raw.github.com/adomokos/light-service/master/resources/light-service.png)
1
+ ![LightService](resources/light-service.png)
2
2
 
3
3
  [![Gem Version](https://img.shields.io/gem/v/light-service.svg)](https://rubygems.org/gems/light-service)
4
4
  [![Build Status](https://secure.travis-ci.org/adomokos/light-service.png)](http://travis-ci.org/adomokos/light-service)
@@ -36,7 +36,7 @@ end
36
36
  ```
37
37
 
38
38
  This controller violates [SRP](http://en.wikipedia.org/wiki/Single_responsibility_principle) all over.
39
- Also, imagine what would it take to test this beast.
39
+ Also, imagine what it would take to test this beast.
40
40
  You could move the tax_percentage finders and calculations into the tax model,
41
41
  but then you'll make your model logic heavy.
42
42
 
@@ -50,19 +50,19 @@ Wouldn't it be nice to see this instead?
50
50
 
51
51
  ```ruby
52
52
  (
53
- LooksUpTaxPercentageAction,
54
- CalculatesOrderTaxAction,
55
- ChecksFreeShippingAction
53
+ LooksUpTaxPercentage,
54
+ CalculatesOrderTax,
55
+ ChecksFreeShipping
56
56
  )
57
57
  ```
58
58
 
59
59
  This block of code should tell you the "story" of what's going on in this workflow.
60
60
  With the help of LightService you can write code this way. First you need an organizer object that sets up the actions in order
61
- and executes them one-by-one. Then you need to create the actions which will only have one method and will do only one thing.
61
+ and executes them one-by-one. Then you need to create the actions with one method (that will do only one thing).
62
62
 
63
- This is how the organizer and actions interact with eachother:
63
+ This is how the organizer and actions interact with each other:
64
64
 
65
- ![LightService](https://raw.github.com/adomokos/light-service/master/resources/organizer_and_actions.png)
65
+ ![LightService](resources/organizer_and_actions.png)
66
66
 
67
67
  ```ruby
68
68
  class CalculatesTax
@@ -157,6 +157,7 @@ simple and elegant Rails code where I told the story of how LightService was ext
157
157
  * [Error Codes](#error-codes)
158
158
  * [Action Rollback](#action-rollback)
159
159
  * [Localizing Messages](#localizing-messages)
160
+ * [Orchestrators](#orchestrators)
160
161
 
161
162
  ## Stopping the Series of Actions
162
163
  When nothing unexpected happens during the organizer's call, the returned `context` will be successful. Here is how you can check for this:
@@ -192,7 +193,7 @@ class SubmitsOrderAction
192
193
  expects :order, :mailer
193
194
 
194
195
  executed do |context|
195
- unless context.order.submit_order_succeful?
196
+ unless context.order.submit_order_successful?
196
197
  context.fail!("Failed to submit the order")
197
198
  next context
198
199
  end
@@ -201,7 +202,7 @@ class SubmitsOrderAction
201
202
  end
202
203
  end
203
204
  ```
204
- ![LightService](https://raw.github.com/adomokos/light-service/master/resources/fail_actions.png)
205
+ ![LightService](resources/fail_actions.png)
205
206
 
206
207
  In the example above the organizer called 4 actions. The first 2 actions got executed successfully. The 3rd had a failure, that pushed the context into a failure state and the 4th action was skipped.
207
208
 
@@ -221,7 +222,7 @@ class ChecksOrderStatusAction
221
222
  end
222
223
  end
223
224
  ```
224
- ![LightService](https://raw.github.com/adomokos/light-service/master/resources/skip_actions.png)
225
+ ![LightService](resources/skip_actions.png)
225
226
 
226
227
  In the example above the organizer called 4 actions. The first 2 actions got executed successfully. The 3rd decided to skip the rest, the 4th action was not invoked. The context was successful.
227
228
 
@@ -316,7 +317,7 @@ class FooAction
316
317
  end
317
318
  ```
318
319
 
319
- Take a look at [this spec](https://github.com/adomokos/light-service/blob/master/spec/action_expects_and_promises_spec.rb) to see the refactoring in action.
320
+ Take a look at [this spec](spec/action_expects_and_promises_spec.rb) to see the refactoring in action.
320
321
 
321
322
  ## Key Aliases
322
323
  The `aliases` macro sets up pairs of keys and aliases in an organizer. Actions can access the context using the aliases.
@@ -329,7 +330,7 @@ Say for example you have actions `AnAction` and `AnotherAction` that you've used
329
330
  class AnOrganizer
330
331
  extend LightService::Organizer
331
332
 
332
- aliases my_key: :key_alias
333
+ aliases :my_key => :key_alias
333
334
 
334
335
  def self.call(order)
335
336
  with(:order => order).reduce(
@@ -492,7 +493,7 @@ Using the `rolled_back` macro is optional for the actions in the chain. You shou
492
493
 
493
494
  The actions are rolled back in reversed order from the point of failure starting with the action that triggered it.
494
495
 
495
- See [this](https://github.com/adomokos/light-service/blob/master/spec/acceptance/rollback_spec.rb) acceptance test to learn more about this functionality.
496
+ See [this](spec/acceptance/rollback_spec.rb) acceptance test to learn more about this functionality.
496
497
 
497
498
  ## Localizing Messages
498
499
  By default LightService provides a mechanism for easily translating your error or success messages via I18n. You can also provide your own custom localization adapter if your application's logic is more complex than what is shown here.
@@ -571,7 +572,96 @@ end
571
572
 
572
573
  To get the value of a `fail!` or `succeed!` message, simply call `#message` on the returned context.
573
574
 
575
+ ## Orchestrators
576
+
577
+ The Organizer - Action combination works really well for simple use cases. However, as business logic gets more complex, or when LightService is used in an ETL workflow, the code that routes the different organizers becomes very complex and imperative. Let's look at a piece of code that does basic data transformations:
578
+
579
+ ```ruby
580
+ class ExtractsTransformsLoadsData
581
+ def self.run(connection)
582
+ context = RetrievesConnectionInfo.call(connection)
583
+ context = PullsDataFromRemoteApi.call(context)
584
+
585
+ retrieved_items = context.retrieved_items
586
+ if retrieved_items.empty?
587
+ NotifiesEngineeringTeamAction.execute(context)
588
+ end
589
+
590
+ retrieved_items.each do |item|
591
+ context[:item] = item
592
+ TransformsData.call(context)
593
+ end
594
+
595
+ context = LoadsData.call(context)
596
+
597
+ SendsNotifications.call(context)
598
+ end
599
+ end
600
+ ```
601
+
602
+ The `LightService::Context` is initialized with the first action, that context is passed around among organizers and actions. This code is still simpler than many out there, but it feels very imperative: it has conditionals, iterators in it. Let's see how we could make it a bit more simpler with a declarative style:
603
+
604
+ ```ruby
605
+ class ExtractsTransformsLoadsData
606
+ extend LightService::Orchestrator
607
+
608
+ def self.run(connection)
609
+ with(:connection => connection).reduce(steps)
610
+ end
611
+
612
+ def self.steps
613
+ [
614
+ RetrievesConnectionInfo,
615
+ PullsDataFromRemoteApi,
616
+ reduce_if(->(ctx) { ctx.retrieved_items.empty? }, [
617
+ NotifiesEngineeringTeamAction
618
+ ]),
619
+ iterate(:retrieved_item, [
620
+ TransformsData
621
+ ]),
622
+ LoadsData,
623
+ SendsNotifications
624
+ ]
625
+ end
626
+ end
627
+ ```
628
+
629
+ This code is much easier to reason about, it's less noisy and it captures the goal of LightService well: simple, declarative code that's easy to understand.
630
+
631
+ Our convention for naming the public methods on the different items at different levels is this:
632
+ ```
633
+ Orchestrators
634
+ |-> run - steps
635
+ Organizers
636
+ |-> call - actions
637
+ Actions
638
+ |-> execute
639
+ ```
640
+
641
+ You can mix organizers with actions in the orchestrator steps, but mixing other organizers with actions in an organizer is discouraged for the sake of simplicity.
642
+
643
+ The 5 different constructs an orchestrator can have:
644
+
645
+ 1. `reduce`
646
+ 2. `reduce_until`
647
+ 3. `reduce_if`
648
+ 4. `iterate`
649
+ 5. `execute`
650
+
651
+ The `reduce` method needs no interaction, it behaves similarly to organizers' `reduce` method.
652
+
653
+ `reduce_until` behaves like a while loop in imperative languages, it iterates until the provided predicate in the lambda evaluates to true. Take a look at [this acceptance test](spec/acceptance/orchestrator/reduce_until_spec.rb) to see how it's used.
654
+
655
+ `reduce_if` will reduce the included organizers and/or actions if the predicate in the labmda evaulates to true. [This acceptance test](spec/acceptance/orchestrator/reduce_if_spec.rb) describes this functionality.
656
+
657
+ `iterate` gives your iteration logic, the symbol you define there has to be in the context as a key. For example. to iterate over items you will use `iterate(:items)` in your steps, the context needs to have `items` as a key, otherwise it will fail. The orchestrator will singularize the collection name and will put the actual item into the context under that name. Remaining with the example above, each element will be accessible by the name `item` for the actions in the `iterate` steps. [This acceptance test](spec/acceptance/orchestrator/iterate_spec.rb) should provide you with an example.
658
+
659
+ To take advantage of another organizer or action, you might need to tweak the context a bit. Let's say you have a hash, and you need to iterate over its values in a series of action. To alter the context and have the values assigned into a variable, you need to create a new action with 1 line of code in it. That seems a lot of seremony for a simple change. You can do that in a `execute` method like this `execute(->(ctx) { ctx[:some_values] = ctx.some_hash.values })`. [This test](spec/acceptance/orchestrator/execute_spec.rb) describes how you can use it.
660
+
661
+ ** Thanks to [@bwvoss](https://github.com/bwvoss) for writing most of the Orchestrators code, I only ported his changes to LS and submitted the PR.
662
+
574
663
  ## Requirements
664
+
575
665
  This gem requires ruby 2.x
576
666
 
577
667
  ## Installation
@@ -12,3 +12,4 @@ require 'light-service/organizer/with_reducer_log_decorator'
12
12
  require 'light-service/organizer/with_reducer_factory'
13
13
  require 'light-service/action'
14
14
  require 'light-service/organizer'
15
+ require 'light-service/orchestrator'
@@ -1,6 +1,7 @@
1
1
  module LightService
2
2
  class Configuration
3
3
  class << self
4
+ attr_accessor :capture_errors
4
5
  attr_writer :logger, :localization_adapter
5
6
 
6
7
  def logger
@@ -49,6 +49,11 @@ module LightService
49
49
  @skip_all
50
50
  end
51
51
 
52
+ def reset_skip_all!
53
+ @message = nil
54
+ @skip_all = false
55
+ end
56
+
52
57
  def outcome
53
58
  msg = '`Context#outcome` attribute reader is ' \
54
59
  'DEPRECATED and will be removed'
@@ -0,0 +1,79 @@
1
+ module LightService
2
+ module Orchestrator
3
+ def self.extended(base_class)
4
+ base_class.extend ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+ def with(data = {})
9
+ @context = LightService::Context.make(data)
10
+ self
11
+ end
12
+
13
+ def reduce(steps, context = @context)
14
+ steps.each_with_object(context) do |step, ctx|
15
+ if step.respond_to?(:execute)
16
+ step.execute(ctx)
17
+ elsif step.respond_to?(:call)
18
+ step.call(ctx)
19
+ else
20
+ raise 'Pass either an action or organizer'
21
+ end
22
+ end
23
+ end
24
+
25
+ def reduce_until(condition_block, steps)
26
+ lambda do |ctx|
27
+ loop do
28
+ ctx = scoped_reduction(ctx, steps)
29
+ break if condition_block.call(ctx) || ctx.failure?
30
+ end
31
+
32
+ ctx
33
+ end
34
+ end
35
+
36
+ def reduce_if(condition_block, steps)
37
+ lambda do |ctx|
38
+ ctx = scoped_reduction(ctx, steps) if condition_block.call(ctx)
39
+ ctx
40
+ end
41
+ end
42
+
43
+ def execute(code_block)
44
+ lambda do |ctx|
45
+ ctx = code_block.call(ctx)
46
+ ctx
47
+ end
48
+ end
49
+
50
+ def iterate(collection_key, steps)
51
+ lambda do |ctx|
52
+ collection = ctx[collection_key]
53
+ item_key = collection_key.to_s.singularize.to_sym
54
+ collection.each do |item|
55
+ ctx[item_key] = item
56
+ ctx = scoped_reduction(ctx, steps)
57
+ end
58
+
59
+ ctx
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ def scoped_reduction(ctx, steps)
66
+ ctx.reset_skip_all! unless ctx.failure?
67
+ ctx =
68
+ if steps.is_a?(Array)
69
+ reduce(steps, ctx)
70
+ else
71
+ reduce([steps], ctx)
72
+ end
73
+ ctx.reset_skip_all! unless ctx.failure?
74
+
75
+ ctx
76
+ end
77
+ end
78
+ end
79
+ end
@@ -48,7 +48,7 @@ module LightService
48
48
  def invoke_action(current_context, action)
49
49
  return action.execute(current_context) unless around_each_handler
50
50
 
51
- around_each_handler.call(action, current_context) do
51
+ around_each_handler.call(current_context) do
52
52
  action.execute(current_context)
53
53
  end
54
54
  end
@@ -1,3 +1,3 @@
1
1
  module LightService
2
- VERSION = "0.7.0".freeze
2
+ VERSION = "0.8.0".freeze
3
3
  end
@@ -20,6 +20,6 @@ Gem::Specification.new do |gem|
20
20
 
21
21
  gem.add_development_dependency("rspec", "~> 3.0")
22
22
  gem.add_development_dependency("simplecov", "~> 0.11")
23
- gem.add_development_dependency("rubocop", "~> 0.36")
23
+ gem.add_development_dependency("rubocop", "~> 0.42")
24
24
  gem.add_development_dependency("pry", "~> 0.10")
25
25
  end
@@ -1,27 +1,19 @@
1
1
  require 'spec_helper'
2
2
  require 'test_doubles'
3
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
4
+ describe 'Executing arbitrary code around each action' do
5
+ it 'can be used to log data' do
6
+ context = { :number => 0, :logger => TestDoubles::TestLogger.new }
21
7
 
22
8
  result = TestDoubles::AroundEachOrganizer.call(context)
23
9
 
24
10
  expect(result.fetch(:number)).to eq(2)
11
+ expect(result[:logger].logs).to eq(
12
+ [{
13
+ :action => TestDoubles::AddsTwoActionWithFetch,
14
+ :before => 0,
15
+ :after => 2
16
+ }]
17
+ )
25
18
  end
26
19
  end
27
-
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+ require 'test_doubles'
3
+
4
+ describe LightService::Orchestrator do
5
+ class TestSkipState
6
+ extend LightService::Orchestrator
7
+ def self.run_skip_before
8
+ with(:number => 1).reduce([
9
+ TestDoubles::SkipAllAction,
10
+ reduce_until(->(ctx) { ctx.number == 3 },
11
+ TestDoubles::AddOneAction)
12
+ ])
13
+ end
14
+
15
+ def self.run_skip_after
16
+ with(:number => 1).reduce([
17
+ TestDoubles::SkipAllAction,
18
+ reduce_until(->(ctx) { ctx.number == 3 }, [
19
+ TestDoubles::AddOneAction,
20
+ TestDoubles::SkipAllAction
21
+ ]),
22
+ TestDoubles::AddOneAction
23
+ ])
24
+ end
25
+
26
+ def self.run_failure
27
+ with(:number => 1).reduce([
28
+ TestDoubles::FailureAction,
29
+ reduce_until(->(ctx) { ctx[:number] == 3 },
30
+ TestDoubles::AddOneAction),
31
+ TestDoubles::AddOneAction
32
+ ])
33
+ end
34
+ end
35
+
36
+ it 'does not skip nested contexts' do
37
+ result = TestSkipState.run_skip_before
38
+
39
+ expect(result).to be_success
40
+ expect(result.number).to eq(3)
41
+ end
42
+
43
+ it 'does not skip after a nested context' do
44
+ result = TestSkipState.run_skip_after
45
+
46
+ expect(result).to be_success
47
+ expect(result.number).to eq(4)
48
+ end
49
+
50
+ it 'respects failure across all nestings' do
51
+ result = TestSkipState.run_failure
52
+
53
+ expect(result).to be_failure
54
+ expect(result[:number]).to eq(1)
55
+ end
56
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+ require 'test_doubles'
3
+
4
+ describe LightService::Orchestrator do
5
+ class TestExecute
6
+ extend LightService::Orchestrator
7
+
8
+ def self.run(context)
9
+ with(context).reduce(steps)
10
+ end
11
+
12
+ def self.steps
13
+ [
14
+ TestDoubles::AddOneAction,
15
+ execute(->(ctx) { ctx.number += 1 }),
16
+ execute(->(ctx) { ctx[:something] = 'hello' }),
17
+ TestDoubles::AddOneAction
18
+ ]
19
+ end
20
+ end
21
+
22
+ it 'calls the lambda in the execute block using the context' do
23
+ result = TestExecute.run(:number => 0)
24
+
25
+ expect(result).to be_success
26
+ expect(result.number).to eq(3)
27
+ expect(result[:something]).to eq('hello')
28
+ end
29
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+ require 'test_doubles'
3
+
4
+ describe LightService::Orchestrator do
5
+ class TestIterate
6
+ extend LightService::Orchestrator
7
+
8
+ def self.run(context)
9
+ with(context).reduce([
10
+ iterate(:numbers, [
11
+ TestDoubles::AddOneAction
12
+ ])
13
+ ])
14
+ end
15
+
16
+ def self.run_single(context)
17
+ with(context).reduce([
18
+ iterate(:numbers, TestDoubles::AddOneAction)
19
+ ])
20
+ end
21
+ end
22
+
23
+ it 'reduces each item of a collection and singularizes the collection key' do
24
+ result = TestIterate.run(:numbers => [1, 2, 3, 4])
25
+
26
+ expect(result).to be_success
27
+ expect(result.number).to eq(5)
28
+ end
29
+
30
+ it 'accepts a single action or organizer' do
31
+ result = TestIterate.run_single(:numbers => [1, 2, 3, 4])
32
+
33
+ expect(result).to be_success
34
+ expect(result.number).to eq(5)
35
+ end
36
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+ require 'test_doubles'
3
+
4
+ describe LightService::Orchestrator do
5
+ class TestReduce
6
+ extend LightService::Orchestrator
7
+
8
+ def self.run(context, steps)
9
+ with(context).reduce(steps)
10
+ end
11
+ end
12
+
13
+ it 'responds to both actions and organizers' do
14
+ result = TestReduce.run({ :number => 0 }, [
15
+ TestDoubles::AddTwoOrganizer,
16
+ TestDoubles::AddOneAction
17
+ ])
18
+
19
+ expect(result).to be_success
20
+ expect(result.number).to eq(3)
21
+ end
22
+
23
+ it 'fails fast by skipping proceeding actions/organizers after failure' do
24
+ result = TestReduce.run({ :number => 0 }, [
25
+ TestDoubles::AddTwoOrganizer,
26
+ TestDoubles::FailureAction,
27
+ TestDoubles::AddOneAction
28
+ ])
29
+
30
+ expect(result).not_to be_success
31
+ expect(result.number).to eq(2)
32
+ end
33
+
34
+ it 'does not allow anything but actions and organizers' do
35
+ expect do
36
+ TestReduce.run({}, [double])
37
+ end.to raise_error(RuntimeError)
38
+ end
39
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+ require 'test_doubles'
3
+
4
+ describe LightService::Orchestrator do
5
+ class TestReduceIf
6
+ extend LightService::Orchestrator
7
+
8
+ def self.run(context)
9
+ with(context).reduce(steps)
10
+ end
11
+
12
+ def self.steps
13
+ [
14
+ TestDoubles::AddOneAction,
15
+ reduce_if(->(ctx) { ctx.number == 1 },
16
+ TestDoubles::AddOneAction)
17
+ ]
18
+ end
19
+ end
20
+
21
+ it 'reduces if the block evaluates to true' do
22
+ result = TestReduceIf.run(:number => 0)
23
+
24
+ expect(result).to be_success
25
+ expect(result.number).to eq(2)
26
+ end
27
+
28
+ it 'does not reduce if the block evaluates to false' do
29
+ result = TestReduceIf.run(:number => 2)
30
+
31
+ expect(result).to be_success
32
+ expect(result.number).to eq(3)
33
+ end
34
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+ require 'test_doubles'
3
+
4
+ RSpec.describe LightService::Orchestrator do
5
+ class TestReduceUntil
6
+ extend LightService::Orchestrator
7
+
8
+ def self.run
9
+ with(:number => 1).reduce(steps)
10
+ end
11
+
12
+ def self.steps
13
+ [
14
+ reduce_until(->(ctx) { ctx.number == 3 },
15
+ TestDoubles::AddOneAction)
16
+ ]
17
+ end
18
+ end
19
+
20
+ it 'reduces until the block evaluates to true' do
21
+ result = TestReduceUntil.run
22
+
23
+ expect(result).to be_success
24
+ expect(result.number).to eq(3)
25
+ end
26
+ end
@@ -22,7 +22,7 @@ describe LightService::Organizer::WithReducer do
22
22
  it "executes a handler around each action and continues reducing" do
23
23
  expect(action1).to receive(:execute).with(context).and_return(context)
24
24
  expect(TestDoubles::AroundEachNullHandler).to receive(:call)
25
- .with(action1, context).and_yield
25
+ .with(context).and_yield
26
26
 
27
27
  result = described_class.new.with(context)
28
28
  .around_each(TestDoubles::AroundEachNullHandler)
@@ -1,17 +1,76 @@
1
1
  # A collection of Action and Organizer dummies used in specs
2
2
 
3
3
  module TestDoubles
4
+ class RollbackAction
5
+ extend LightService::Action
6
+ executed(&:fail_with_rollback!)
7
+ end
8
+
9
+ class RaiseErrorAction
10
+ extend LightService::Action
11
+ executed do |_ctx|
12
+ raise 'A problem has occured.'
13
+ end
14
+ end
15
+
16
+ class RaiseAnotherErrorAction
17
+ extend LightService::Action
18
+ executed do |_ctx|
19
+ raise 'More problems'
20
+ end
21
+ end
22
+
23
+ class SkipAllAction
24
+ extend LightService::Action
25
+ executed(&:skip_all!)
26
+ end
27
+
28
+ class FailureAction
29
+ extend LightService::Action
30
+ executed(&:fail!)
31
+ end
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
+ class AddTwoOrganizer
45
+ extend LightService::Organizer
46
+ def self.call(context)
47
+ with(context).reduce([AddOneAction, AddOneAction])
48
+ end
49
+ end
50
+
4
51
  class AroundEachNullHandler
5
52
  def self.call(_action, _context)
6
53
  yield
7
54
  end
8
55
  end
9
56
 
57
+ class TestLogger
58
+ attr_accessor :logs
59
+ def initialize
60
+ @logs = []
61
+ end
62
+ end
63
+
10
64
  class AroundEachLoggerHandler
11
- def self.call(action, context)
12
- MyLogger.info(action, context)
65
+ def self.call(context)
66
+ before_number = context[:number]
13
67
  result = yield
14
- MyLogger.info(action, context)
68
+
69
+ context[:logger].logs << {
70
+ :action => context.current_action,
71
+ :before => before_number,
72
+ :after => result[:number]
73
+ }
15
74
 
16
75
  result
17
76
  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.7.0
4
+ version: 0.8.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: 2017-01-14 00:00:00.000000000 Z
11
+ date: 2017-03-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0.36'
61
+ version: '0.42'
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.36'
68
+ version: '0.42'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: pry
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -111,6 +111,7 @@ files:
111
111
  - lib/light-service/context/key_verifier.rb
112
112
  - lib/light-service/errors.rb
113
113
  - lib/light-service/localization_adapter.rb
114
+ - lib/light-service/orchestrator.rb
114
115
  - lib/light-service/organizer.rb
115
116
  - lib/light-service/organizer/with_reducer.rb
116
117
  - lib/light-service/organizer/with_reducer_factory.rb
@@ -127,6 +128,12 @@ files:
127
128
  - spec/acceptance/log_from_organizer_spec.rb
128
129
  - spec/acceptance/message_localization_spec.rb
129
130
  - spec/acceptance/not_having_call_method_warning_spec.rb
131
+ - spec/acceptance/orchestrator/context_failure_and_skipping_spec.rb
132
+ - spec/acceptance/orchestrator/execute_spec.rb
133
+ - spec/acceptance/orchestrator/iterate_spec.rb
134
+ - spec/acceptance/orchestrator/organizer_action_combination_spec.rb
135
+ - spec/acceptance/orchestrator/reduce_if_spec.rb
136
+ - spec/acceptance/orchestrator/reduce_until_spec.rb
130
137
  - spec/acceptance/rollback_spec.rb
131
138
  - spec/action_expected_keys_spec.rb
132
139
  - spec/action_expects_and_promises_spec.rb
@@ -178,6 +185,12 @@ test_files:
178
185
  - spec/acceptance/log_from_organizer_spec.rb
179
186
  - spec/acceptance/message_localization_spec.rb
180
187
  - spec/acceptance/not_having_call_method_warning_spec.rb
188
+ - spec/acceptance/orchestrator/context_failure_and_skipping_spec.rb
189
+ - spec/acceptance/orchestrator/execute_spec.rb
190
+ - spec/acceptance/orchestrator/iterate_spec.rb
191
+ - spec/acceptance/orchestrator/organizer_action_combination_spec.rb
192
+ - spec/acceptance/orchestrator/reduce_if_spec.rb
193
+ - spec/acceptance/orchestrator/reduce_until_spec.rb
181
194
  - spec/acceptance/rollback_spec.rb
182
195
  - spec/action_expected_keys_spec.rb
183
196
  - spec/action_expects_and_promises_spec.rb