light-service 0.8.1 → 0.8.2
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/README.md +10 -4
- data/RELEASES.md +6 -0
- data/lib/light-service/action.rb +3 -1
- data/lib/light-service/context.rb +22 -0
- data/lib/light-service/orchestrator.rb +26 -0
- data/lib/light-service/version.rb +1 -1
- data/light-service.gemspec +1 -1
- data/spec/acceptance/fail_spec.rb +24 -0
- data/spec/acceptance/orchestrator/context_failure_and_skipping_spec.rb +7 -7
- data/spec/acceptance/orchestrator/execute_spec.rb +17 -0
- data/spec/acceptance/orchestrator/iterate_spec.rb +17 -0
- data/spec/acceptance/orchestrator/reduce_if_spec.rb +19 -2
- data/spec/acceptance/orchestrator/reduce_until_spec.rb +20 -3
- data/spec/acceptance/orchestrator/with_callback_spec.rb +169 -0
- data/spec/context/inspect_spec.rb +57 -0
- data/spec/context_spec.rb +2 -2
- metadata +12 -6
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 65ef2c9c99bebb3e4b19a0fbfc63c893381fbfe4
         | 
| 4 | 
            +
              data.tar.gz: 6962376a614207f0475112a1f21c1872876ec865
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 7ea257de8c459ecccbcb0d702c61c603f45c3d6bc50f2a829925ecbe2af4e1fbeb0eee277a9005c99a1f928087610ce02e8317b63e13e329f8b4c2328958b997
         | 
| 7 | 
            +
              data.tar.gz: 768698a21f441add9d09ec47eed6cc8384b1babb232c1778a3abbdf0531ac8179eb3b47c75c16e07db77b2fe14870fd29e65cd022b45f9b458e02e8a92a9d77e
         | 
    
        data/README.md
    CHANGED
    
    | @@ -187,6 +187,9 @@ When something goes wrong in an action and you want to halt the chain, you need | |
| 187 187 | 
             
            The context's `fail!` method can take an optional message argument, this message might help describing what went wrong.
         | 
| 188 188 | 
             
            In case you need to return immediately from the point of failure, you have to do that by calling `next context`.
         | 
| 189 189 |  | 
| 190 | 
            +
            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.
         | 
| 191 | 
            +
            This will immediately leave the block, you don't need to call `next context` to return from the block.
         | 
| 192 | 
            +
             | 
| 190 193 | 
             
            Here is an example:
         | 
| 191 194 | 
             
            ```ruby
         | 
| 192 195 | 
             
            class SubmitsOrderAction
         | 
| @@ -195,10 +198,10 @@ class SubmitsOrderAction | |
| 195 198 |  | 
| 196 199 | 
             
              executed do |context|
         | 
| 197 200 | 
             
                unless context.order.submit_order_successful?
         | 
| 198 | 
            -
                  context. | 
| 199 | 
            -
                  next context
         | 
| 201 | 
            +
                  context.fail_and_return!("Failed to submit the order")
         | 
| 200 202 | 
             
                end
         | 
| 201 203 |  | 
| 204 | 
            +
                # This won't be executed
         | 
| 202 205 | 
             
                context.mailer.send_order_notification!
         | 
| 203 206 | 
             
              end
         | 
| 204 207 | 
             
            end
         | 
| @@ -641,15 +644,16 @@ Orchestrators | |
| 641 644 |  | 
| 642 645 | 
             
            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.
         | 
| 643 646 |  | 
| 644 | 
            -
            The  | 
| 647 | 
            +
            The 6 different constructs an orchestrator can have:
         | 
| 645 648 |  | 
| 646 649 | 
             
            1. `reduce`
         | 
| 647 650 | 
             
            2. `reduce_until`
         | 
| 648 651 | 
             
            3. `reduce_if`
         | 
| 649 652 | 
             
            4. `iterate`
         | 
| 650 653 | 
             
            5. `execute`
         | 
| 654 | 
            +
            6. `with_callback`
         | 
| 651 655 |  | 
| 652 | 
            -
            The `reduce` method needs no  | 
| 656 | 
            +
            The `reduce` method needs no introduction, it behaves similarly to organizers' `reduce` method.
         | 
| 653 657 |  | 
| 654 658 | 
             
            `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.
         | 
| 655 659 |  | 
| @@ -659,6 +663,8 @@ The `reduce` method needs no interaction, it behaves similarly to organizers' `r | |
| 659 663 |  | 
| 660 664 | 
             
            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 ceremony 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.
         | 
| 661 665 |  | 
| 666 | 
            +
            Use `with_callback` when you want to execute actions with a deferred and controlled callback. It works similar to a Sax parser, I've used it for processing large files. The advantage of it is not having to keep large amount of data in memory. See [this acceptance test](spec/acceptance/orchestrator/with_callback_spec.rb) as a working example.
         | 
| 667 | 
            +
             | 
| 662 668 | 
             
            ** 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.
         | 
| 663 669 |  | 
| 664 670 | 
             
            ## ContextFactory for Faster Action Testing
         | 
    
        data/RELEASES.md
    CHANGED
    
    | @@ -1,5 +1,11 @@ | |
| 1 1 | 
             
            A brief list of new features and changes introduced with the specified version.
         | 
| 2 2 |  | 
| 3 | 
            +
            ### 0.8.2
         | 
| 4 | 
            +
            * A better way to [inspect](https://github.com/adomokos/light-service/pull/110) the context.
         | 
| 5 | 
            +
            * [Short-circuiting](https://github.com/adomokos/light-service/pull/113) the Orchestrator methods.
         | 
| 6 | 
            +
            * [Fail and return - with one call](https://github.com/adomokos/light-service/pull/115), no `and next` is needed.
         | 
| 7 | 
            +
            * Adding [with_callback](https://github.com/adomokos/light-service/pull/116) to Orchestrators, allows us to process large data in smaller chunks.
         | 
| 8 | 
            +
             | 
| 3 9 | 
             
            ### 0.8.1
         | 
| 4 10 | 
             
            * Renaming `skip_all!` to [skip_remaining!](https://github.com/adomokos/light-service/pull/103).
         | 
| 5 11 | 
             
            * Adding [ContextFactory](https://github.com/adomokos/light-service/pull/107) for easier testing.
         | 
    
        data/lib/light-service/action.rb
    CHANGED
    
    
| @@ -87,6 +87,11 @@ module LightService | |
| 87 87 | 
             
                  @outcome = Outcomes::FAILURE
         | 
| 88 88 | 
             
                end
         | 
| 89 89 |  | 
| 90 | 
            +
                def fail_and_return!(*args)
         | 
| 91 | 
            +
                  fail!(*args)
         | 
| 92 | 
            +
                  throw(:jump_when_failed, *args)
         | 
| 93 | 
            +
                end
         | 
| 94 | 
            +
             | 
| 90 95 | 
             
                def fail_with_rollback!(message = nil, error_code = nil)
         | 
| 91 96 | 
             
                  fail!(message, error_code)
         | 
| 92 97 | 
             
                  raise FailWithRollbackError
         | 
| @@ -138,6 +143,23 @@ module LightService | |
| 138 143 | 
             
                def fetch(key, default_or_block = nil)
         | 
| 139 144 | 
             
                  self[key] ||= super(key, default_or_block)
         | 
| 140 145 | 
             
                end
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                def inspect
         | 
| 148 | 
            +
                  "#{self.class}(#{self}, " \
         | 
| 149 | 
            +
                  + "success: #{success?}, " \
         | 
| 150 | 
            +
                  + "message: #{check_nil(message)}, " \
         | 
| 151 | 
            +
                  + "error_code: #{check_nil(error_code)}, " \
         | 
| 152 | 
            +
                  + "skip_remaining: #{@skip_remaining}, " \
         | 
| 153 | 
            +
                  + "aliases: #{@aliases}" \
         | 
| 154 | 
            +
                  + ")"
         | 
| 155 | 
            +
                end
         | 
| 156 | 
            +
             | 
| 157 | 
            +
                private
         | 
| 158 | 
            +
             | 
| 159 | 
            +
                def check_nil(value)
         | 
| 160 | 
            +
                  return 'nil' unless value
         | 
| 161 | 
            +
                  "'#{value}'"
         | 
| 162 | 
            +
                end
         | 
| 141 163 | 
             
              end
         | 
| 142 164 | 
             
              # rubocop:enable ClassLength
         | 
| 143 165 | 
             
            end
         | 
| @@ -24,6 +24,8 @@ module LightService | |
| 24 24 |  | 
| 25 25 | 
             
                  def reduce_until(condition_block, steps)
         | 
| 26 26 | 
             
                    lambda do |ctx|
         | 
| 27 | 
            +
                      return ctx if ctx.stop_processing?
         | 
| 28 | 
            +
             | 
| 27 29 | 
             
                      loop do
         | 
| 28 30 | 
             
                        ctx = scoped_reduction(ctx, steps)
         | 
| 29 31 | 
             
                        break if condition_block.call(ctx) || ctx.failure?
         | 
| @@ -35,6 +37,8 @@ module LightService | |
| 35 37 |  | 
| 36 38 | 
             
                  def reduce_if(condition_block, steps)
         | 
| 37 39 | 
             
                    lambda do |ctx|
         | 
| 40 | 
            +
                      return ctx if ctx.stop_processing?
         | 
| 41 | 
            +
             | 
| 38 42 | 
             
                      ctx = scoped_reduction(ctx, steps) if condition_block.call(ctx)
         | 
| 39 43 | 
             
                      ctx
         | 
| 40 44 | 
             
                    end
         | 
| @@ -42,6 +46,8 @@ module LightService | |
| 42 46 |  | 
| 43 47 | 
             
                  def execute(code_block)
         | 
| 44 48 | 
             
                    lambda do |ctx|
         | 
| 49 | 
            +
                      return ctx if ctx.stop_processing?
         | 
| 50 | 
            +
             | 
| 45 51 | 
             
                      ctx = code_block.call(ctx)
         | 
| 46 52 | 
             
                      ctx
         | 
| 47 53 | 
             
                    end
         | 
| @@ -49,6 +55,8 @@ module LightService | |
| 49 55 |  | 
| 50 56 | 
             
                  def iterate(collection_key, steps)
         | 
| 51 57 | 
             
                    lambda do |ctx|
         | 
| 58 | 
            +
                      return ctx if ctx.stop_processing?
         | 
| 59 | 
            +
             | 
| 52 60 | 
             
                      collection = ctx[collection_key]
         | 
| 53 61 | 
             
                      item_key = collection_key.to_s.singularize.to_sym
         | 
| 54 62 | 
             
                      collection.each do |item|
         | 
| @@ -60,6 +68,24 @@ module LightService | |
| 60 68 | 
             
                    end
         | 
| 61 69 | 
             
                  end
         | 
| 62 70 |  | 
| 71 | 
            +
                  def with_callback(action, steps)
         | 
| 72 | 
            +
                    lambda do |ctx|
         | 
| 73 | 
            +
                      return ctx if ctx.stop_processing?
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                      # This will only allow 2 level deep nesting of callbacks
         | 
| 76 | 
            +
                      previous_callback = ctx[:callback]
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                      ctx[:callback] = lambda do |context|
         | 
| 79 | 
            +
                        reduce(steps, context)
         | 
| 80 | 
            +
                      end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                      ctx = action.execute(ctx)
         | 
| 83 | 
            +
                      ctx[:callback] = previous_callback
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                      ctx
         | 
| 86 | 
            +
                    end
         | 
| 87 | 
            +
                  end
         | 
| 88 | 
            +
             | 
| 63 89 | 
             
                  private
         | 
| 64 90 |  | 
| 65 91 | 
             
                  def scoped_reduction(ctx, steps)
         | 
    
        data/light-service.gemspec
    CHANGED
    
    | @@ -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.14.1")
         | 
| 23 | 
            -
              gem.add_development_dependency("rubocop", " | 
| 23 | 
            +
              gem.add_development_dependency("rubocop", "0.46")
         | 
| 24 24 | 
             
              gem.add_development_dependency("pry", "~> 0.10")
         | 
| 25 25 | 
             
            end
         | 
| @@ -0,0 +1,24 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            RSpec.describe "fail! returns immediately from executed block" do
         | 
| 4 | 
            +
              class FailAction
         | 
| 5 | 
            +
                extend LightService::Action
         | 
| 6 | 
            +
                promises :one, :two
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                executed do |ctx|
         | 
| 9 | 
            +
                  ctx.one = 1
         | 
| 10 | 
            +
                  # Have to set it in Context
         | 
| 11 | 
            +
                  ctx.two = nil
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  ctx.fail_and_return!('Something went wrong')
         | 
| 14 | 
            +
                  ctx.two = 2
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              it "returns immediately from executed block" do
         | 
| 19 | 
            +
                result = FailAction.execute
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                expect(result).to be_failure
         | 
| 22 | 
            +
                expect(result.two).to be_nil
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
            end
         | 
| @@ -14,11 +14,11 @@ describe LightService::Orchestrator do | |
| 14 14 |  | 
| 15 15 | 
             
                def self.run_skip_after
         | 
| 16 16 | 
             
                  with(:number => 1).reduce([
         | 
| 17 | 
            -
                                              TestDoubles:: | 
| 17 | 
            +
                                              TestDoubles::AddOneAction,
         | 
| 18 18 | 
             
                                              reduce_until(->(ctx) { ctx.number == 3 }, [
         | 
| 19 | 
            -
                                                             TestDoubles::AddOneAction | 
| 20 | 
            -
                                                             TestDoubles::SkipAllAction
         | 
| 19 | 
            +
                                                             TestDoubles::AddOneAction
         | 
| 21 20 | 
             
                                                           ]),
         | 
| 21 | 
            +
                                              TestDoubles::SkipAllAction,
         | 
| 22 22 | 
             
                                              TestDoubles::AddOneAction
         | 
| 23 23 | 
             
                                            ])
         | 
| 24 24 | 
             
                end
         | 
| @@ -33,18 +33,18 @@ describe LightService::Orchestrator do | |
| 33 33 | 
             
                end
         | 
| 34 34 | 
             
              end
         | 
| 35 35 |  | 
| 36 | 
            -
              it ' | 
| 36 | 
            +
              it 'skips all the rest of the actions' do
         | 
| 37 37 | 
             
                result = TestSkipState.run_skip_before
         | 
| 38 38 |  | 
| 39 39 | 
             
                expect(result).to be_success
         | 
| 40 | 
            -
                expect(result | 
| 40 | 
            +
                expect(result[:number]).to eq(1)
         | 
| 41 41 | 
             
              end
         | 
| 42 42 |  | 
| 43 | 
            -
              it ' | 
| 43 | 
            +
              it 'skips after an action in nested context' do
         | 
| 44 44 | 
             
                result = TestSkipState.run_skip_after
         | 
| 45 45 |  | 
| 46 46 | 
             
                expect(result).to be_success
         | 
| 47 | 
            -
                expect(result | 
| 47 | 
            +
                expect(result[:number]).to eq(3)
         | 
| 48 48 | 
             
              end
         | 
| 49 49 |  | 
| 50 50 | 
             
              it 'respects failure across all nestings' do
         | 
| @@ -19,6 +19,8 @@ describe LightService::Orchestrator do | |
| 19 19 | 
             
                end
         | 
| 20 20 | 
             
              end
         | 
| 21 21 |  | 
| 22 | 
            +
              let(:empty_context) { LightService::Context.make }
         | 
| 23 | 
            +
             | 
| 22 24 | 
             
              it 'calls the lambda in the execute block using the context' do
         | 
| 23 25 | 
             
                result = TestExecute.run(:number => 0)
         | 
| 24 26 |  | 
| @@ -26,4 +28,19 @@ describe LightService::Orchestrator do | |
| 26 28 | 
             
                expect(result.number).to eq(3)
         | 
| 27 29 | 
             
                expect(result[:something]).to eq('hello')
         | 
| 28 30 | 
             
              end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              it 'will not execute a failed context' do
         | 
| 33 | 
            +
                empty_context.fail!('Something bad happened')
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                result = TestExecute.run(empty_context)
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                expect(result).to be_failure
         | 
| 38 | 
            +
              end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
              it 'does not execute over a skipped context' do
         | 
| 41 | 
            +
                empty_context.skip_remaining!('No more needed')
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                result = TestExecute.run(empty_context)
         | 
| 44 | 
            +
                expect(result).to be_success
         | 
| 45 | 
            +
              end
         | 
| 29 46 | 
             
            end
         | 
| @@ -20,6 +20,8 @@ describe LightService::Orchestrator do | |
| 20 20 | 
             
                end
         | 
| 21 21 | 
             
              end
         | 
| 22 22 |  | 
| 23 | 
            +
              let(:empty_context) { LightService::Context.make }
         | 
| 24 | 
            +
             | 
| 23 25 | 
             
              it 'reduces each item of a collection and singularizes the collection key' do
         | 
| 24 26 | 
             
                result = TestIterate.run(:numbers => [1, 2, 3, 4])
         | 
| 25 27 |  | 
| @@ -33,4 +35,19 @@ describe LightService::Orchestrator do | |
| 33 35 | 
             
                expect(result).to be_success
         | 
| 34 36 | 
             
                expect(result.number).to eq(5)
         | 
| 35 37 | 
             
              end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
              it 'will not iterate over a failed context' do
         | 
| 40 | 
            +
                empty_context.fail!('Something bad happened')
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                result = TestIterate.run(empty_context)
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                expect(result).to be_failure
         | 
| 45 | 
            +
              end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
              it 'does not iterate over a skipped context' do
         | 
| 48 | 
            +
                empty_context.skip_remaining!('No more needed')
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                result = TestIterate.run(empty_context)
         | 
| 51 | 
            +
                expect(result).to be_success
         | 
| 52 | 
            +
              end
         | 
| 36 53 | 
             
            end
         | 
| @@ -18,17 +18,34 @@ describe LightService::Orchestrator do | |
| 18 18 | 
             
                end
         | 
| 19 19 | 
             
              end
         | 
| 20 20 |  | 
| 21 | 
            +
              let(:empty_context) { LightService::Context.make }
         | 
| 22 | 
            +
             | 
| 21 23 | 
             
              it 'reduces if the block evaluates to true' do
         | 
| 22 24 | 
             
                result = TestReduceIf.run(:number => 0)
         | 
| 23 25 |  | 
| 24 26 | 
             
                expect(result).to be_success
         | 
| 25 | 
            -
                expect(result | 
| 27 | 
            +
                expect(result[:number]).to eq(2)
         | 
| 26 28 | 
             
              end
         | 
| 27 29 |  | 
| 28 30 | 
             
              it 'does not reduce if the block evaluates to false' do
         | 
| 29 31 | 
             
                result = TestReduceIf.run(:number => 2)
         | 
| 30 32 |  | 
| 31 33 | 
             
                expect(result).to be_success
         | 
| 32 | 
            -
                expect(result | 
| 34 | 
            +
                expect(result[:number]).to eq(3)
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
              it 'will not reduce over a failed context' do
         | 
| 38 | 
            +
                empty_context.fail!('Something bad happened')
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                result = TestReduceIf.run(empty_context)
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                expect(result).to be_failure
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
              it 'does not reduce over a skipped context' do
         | 
| 46 | 
            +
                empty_context.skip_remaining!('No more needed')
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                result = TestReduceIf.run(empty_context)
         | 
| 49 | 
            +
                expect(result).to be_success
         | 
| 33 50 | 
             
              end
         | 
| 34 51 | 
             
            end
         | 
| @@ -5,8 +5,8 @@ RSpec.describe LightService::Orchestrator do | |
| 5 5 | 
             
              class TestReduceUntil
         | 
| 6 6 | 
             
                extend LightService::Orchestrator
         | 
| 7 7 |  | 
| 8 | 
            -
                def self.run
         | 
| 9 | 
            -
                  with( | 
| 8 | 
            +
                def self.run(context)
         | 
| 9 | 
            +
                  with(context).reduce(steps)
         | 
| 10 10 | 
             
                end
         | 
| 11 11 |  | 
| 12 12 | 
             
                def self.steps
         | 
| @@ -17,10 +17,27 @@ RSpec.describe LightService::Orchestrator do | |
| 17 17 | 
             
                end
         | 
| 18 18 | 
             
              end
         | 
| 19 19 |  | 
| 20 | 
            +
              let(:empty_context) { LightService::Context.make }
         | 
| 21 | 
            +
             | 
| 20 22 | 
             
              it 'reduces until the block evaluates to true' do
         | 
| 21 | 
            -
                 | 
| 23 | 
            +
                context = { :number => 1 }
         | 
| 24 | 
            +
                result = TestReduceUntil.run(context)
         | 
| 22 25 |  | 
| 23 26 | 
             
                expect(result).to be_success
         | 
| 24 27 | 
             
                expect(result.number).to eq(3)
         | 
| 25 28 | 
             
              end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
              it 'does not execute on failed context' do
         | 
| 31 | 
            +
                empty_context.fail!('Something bad happened')
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                result = TestReduceUntil.run(empty_context)
         | 
| 34 | 
            +
                expect(result).to be_failure
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
              it 'does not execute a skipped context' do
         | 
| 38 | 
            +
                empty_context.skip_remaining!('No more needed')
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                result = TestReduceUntil.run(empty_context)
         | 
| 41 | 
            +
                expect(result).to be_success
         | 
| 42 | 
            +
              end
         | 
| 26 43 | 
             
            end
         | 
| @@ -0,0 +1,169 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
            require 'test_doubles'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            describe LightService::Orchestrator do
         | 
| 5 | 
            +
              class TestWithCallback
         | 
| 6 | 
            +
                extend LightService::Orchestrator
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                def self.run(context = {})
         | 
| 9 | 
            +
                  with(context).reduce(steps)
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def self.steps
         | 
| 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 | 
            +
              describe 'a simple case with a single callback' do
         | 
| 65 | 
            +
                it 'calls the actions defined with callback' do
         | 
| 66 | 
            +
                  result = TestWithCallback.run
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                  expect(result.counter).to eq(3)
         | 
| 69 | 
            +
                  expect(result.total).to eq(6)
         | 
| 70 | 
            +
                end
         | 
| 71 | 
            +
              end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
              describe 'a more complex example with nested callbacks' do
         | 
| 74 | 
            +
                class TestWithNestedCallback
         | 
| 75 | 
            +
                  extend LightService::Orchestrator
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                  def self.run(context = {})
         | 
| 78 | 
            +
                    with(context).reduce(steps)
         | 
| 79 | 
            +
                  end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                  def self.steps
         | 
| 82 | 
            +
                    [
         | 
| 83 | 
            +
                      SetUpNestedContextAction,
         | 
| 84 | 
            +
                      with_callback(IterateOuterCollectionAction,
         | 
| 85 | 
            +
                                    [IncrementOuterCountAction,
         | 
| 86 | 
            +
                                     with_callback(IterateCollectionAction,
         | 
| 87 | 
            +
                                                   [IncrementCountAction,
         | 
| 88 | 
            +
                                                    AddToTotalAction])])
         | 
| 89 | 
            +
                    ]
         | 
| 90 | 
            +
                  end
         | 
| 91 | 
            +
                end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                class SetUpNestedContextAction
         | 
| 94 | 
            +
                  extend LightService::Action
         | 
| 95 | 
            +
                  promises :outer_numbers, :outer_counter,
         | 
| 96 | 
            +
                           :numbers, :counter, :total
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                  executed do |ctx|
         | 
| 99 | 
            +
                    ctx.outer_numbers = [12, 17]
         | 
| 100 | 
            +
                    ctx.outer_counter = 0
         | 
| 101 | 
            +
                    ctx.numbers = [1, 2, 3]
         | 
| 102 | 
            +
                    ctx.counter = 0
         | 
| 103 | 
            +
                    ctx.total = 0
         | 
| 104 | 
            +
                  end
         | 
| 105 | 
            +
                end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                class IterateOuterCollectionAction
         | 
| 108 | 
            +
                  extend LightService::Action
         | 
| 109 | 
            +
                  expects :outer_numbers, :callback
         | 
| 110 | 
            +
                  promises :outer_number
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                  executed do |ctx|
         | 
| 113 | 
            +
                    ctx.outer_numbers.each do |outer_number|
         | 
| 114 | 
            +
                      ctx.outer_number = outer_number
         | 
| 115 | 
            +
                      ctx.callback.call(ctx)
         | 
| 116 | 
            +
                    end
         | 
| 117 | 
            +
                  end
         | 
| 118 | 
            +
                end
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                class IncrementOuterCountAction
         | 
| 121 | 
            +
                  extend LightService::Action
         | 
| 122 | 
            +
                  expects :outer_counter
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                  executed do |ctx|
         | 
| 125 | 
            +
                    ctx.outer_counter = ctx.outer_counter + 1
         | 
| 126 | 
            +
                  end
         | 
| 127 | 
            +
                end
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                it 'calls both the action and the nested callbacks' do
         | 
| 130 | 
            +
                  result = TestWithNestedCallback.run
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                  expect(result.outer_counter).to eq(2)
         | 
| 133 | 
            +
                  # Counts and total are the duplicates of
         | 
| 134 | 
            +
                  # what you'll see in the simple spec,
         | 
| 135 | 
            +
                  # as the internal callback logic is called
         | 
| 136 | 
            +
                  # twice due to 2 items in the outer_numbers
         | 
| 137 | 
            +
                  # collection.
         | 
| 138 | 
            +
                  expect(result.counter).to eq(6)
         | 
| 139 | 
            +
                  expect(result.total).to eq(12)
         | 
| 140 | 
            +
                end
         | 
| 141 | 
            +
              end
         | 
| 142 | 
            +
             | 
| 143 | 
            +
              describe 'with failed or skipped context' do
         | 
| 144 | 
            +
                class TestWithFailureCallback
         | 
| 145 | 
            +
                  extend LightService::Orchestrator
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                  def self.run(context = {})
         | 
| 148 | 
            +
                    with(context).reduce(steps)
         | 
| 149 | 
            +
                  end
         | 
| 150 | 
            +
             | 
| 151 | 
            +
                  def self.steps
         | 
| 152 | 
            +
                    [
         | 
| 153 | 
            +
                      SetUpContextAction,
         | 
| 154 | 
            +
                      with_callback(IterateCollectionAction,
         | 
| 155 | 
            +
                                    [IncrementCountAction,
         | 
| 156 | 
            +
                                     TestDoubles::FailureAction])
         | 
| 157 | 
            +
                    ]
         | 
| 158 | 
            +
                  end
         | 
| 159 | 
            +
                end
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                it 'will not process the routine' do
         | 
| 162 | 
            +
                  result = TestWithFailureCallback.run
         | 
| 163 | 
            +
             | 
| 164 | 
            +
                  expect(result).to be_failure
         | 
| 165 | 
            +
                  expect(result.counter).to eq(1)
         | 
| 166 | 
            +
                  expect(result.total).to eq(0)
         | 
| 167 | 
            +
                end
         | 
| 168 | 
            +
              end
         | 
| 169 | 
            +
            end
         | 
| @@ -0,0 +1,57 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
            require 'test_doubles'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            RSpec.describe LightService::Context do
         | 
| 5 | 
            +
              subject(:context) { LightService::Context.make }
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              describe 'to_s' do
         | 
| 8 | 
            +
                it 'prints the context hash' do
         | 
| 9 | 
            +
                  expect(context.to_s).to eq('{}')
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
              end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              describe '#inspect' do
         | 
| 14 | 
            +
                it 'inspects the hash with all the fields' do
         | 
| 15 | 
            +
                  inspected_context =
         | 
| 16 | 
            +
                    'LightService::Context({}, ' \
         | 
| 17 | 
            +
                    + 'success: true, ' \
         | 
| 18 | 
            +
                    + 'message: \'\', ' \
         | 
| 19 | 
            +
                    + 'error_code: nil, ' \
         | 
| 20 | 
            +
                    + 'skip_remaining: false, ' \
         | 
| 21 | 
            +
                    + 'aliases: {}' \
         | 
| 22 | 
            +
                    + ')'
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  expect(context.inspect).to eq(inspected_context)
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                it 'prints the error message' do
         | 
| 28 | 
            +
                  context.fail!('There was an error')
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  inspected_context =
         | 
| 31 | 
            +
                    'LightService::Context({}, ' \
         | 
| 32 | 
            +
                    + 'success: false, ' \
         | 
| 33 | 
            +
                    + 'message: \'There was an error\', ' \
         | 
| 34 | 
            +
                    + 'error_code: nil, ' \
         | 
| 35 | 
            +
                    + 'skip_remaining: false, ' \
         | 
| 36 | 
            +
                    + 'aliases: {}' \
         | 
| 37 | 
            +
                    + ')'
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                  expect(context.inspect).to eq(inspected_context)
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                it 'prints skip_remaining' do
         | 
| 43 | 
            +
                  context.skip_remaining!('No need to process')
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                  inspected_context =
         | 
| 46 | 
            +
                    'LightService::Context({}, ' \
         | 
| 47 | 
            +
                    + 'success: true, ' \
         | 
| 48 | 
            +
                    + 'message: \'No need to process\', ' \
         | 
| 49 | 
            +
                    + 'error_code: nil, ' \
         | 
| 50 | 
            +
                    + 'skip_remaining: true, ' \
         | 
| 51 | 
            +
                    + 'aliases: {}' \
         | 
| 52 | 
            +
                    + ')'
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                  expect(context.inspect).to eq(inspected_context)
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
              end
         | 
| 57 | 
            +
            end
         | 
    
        data/spec/context_spec.rb
    CHANGED
    
    
    
        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.8. | 
| 4 | 
            +
              version: 0.8.2
         | 
| 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- | 
| 11 | 
            +
            date: 2017-08-17 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: activesupport
         | 
| @@ -56,16 +56,16 @@ dependencies: | |
| 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. | 
| 61 | 
            +
                    version: '0.46'
         | 
| 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. | 
| 68 | 
            +
                    version: '0.46'
         | 
| 69 69 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 70 70 | 
             
              name: pry
         | 
| 71 71 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -126,6 +126,7 @@ files: | |
| 126 126 | 
             
            - resources/skip_actions.png
         | 
| 127 127 | 
             
            - spec/acceptance/add_numbers_spec.rb
         | 
| 128 128 | 
             
            - spec/acceptance/around_each_spec.rb
         | 
| 129 | 
            +
            - spec/acceptance/fail_spec.rb
         | 
| 129 130 | 
             
            - spec/acceptance/include_warning_spec.rb
         | 
| 130 131 | 
             
            - spec/acceptance/log_from_organizer_spec.rb
         | 
| 131 132 | 
             
            - spec/acceptance/message_localization_spec.rb
         | 
| @@ -136,6 +137,7 @@ files: | |
| 136 137 | 
             
            - spec/acceptance/orchestrator/organizer_action_combination_spec.rb
         | 
| 137 138 | 
             
            - spec/acceptance/orchestrator/reduce_if_spec.rb
         | 
| 138 139 | 
             
            - spec/acceptance/orchestrator/reduce_until_spec.rb
         | 
| 140 | 
            +
            - spec/acceptance/orchestrator/with_callback_spec.rb
         | 
| 139 141 | 
             
            - spec/acceptance/rollback_spec.rb
         | 
| 140 142 | 
             
            - spec/acceptance/skip_all_warning_spec.rb
         | 
| 141 143 | 
             
            - spec/acceptance/testing/context_factory_spec.rb
         | 
| @@ -143,6 +145,7 @@ files: | |
| 143 145 | 
             
            - spec/action_expects_and_promises_spec.rb
         | 
| 144 146 | 
             
            - spec/action_promised_keys_spec.rb
         | 
| 145 147 | 
             
            - spec/action_spec.rb
         | 
| 148 | 
            +
            - spec/context/inspect_spec.rb
         | 
| 146 149 | 
             
            - spec/context_spec.rb
         | 
| 147 150 | 
             
            - spec/localization_adapter_spec.rb
         | 
| 148 151 | 
             
            - spec/organizer/with_reducer_spec.rb
         | 
| @@ -186,6 +189,7 @@ summary: A service skeleton with an emphasis on simplicity | |
| 186 189 | 
             
            test_files:
         | 
| 187 190 | 
             
            - spec/acceptance/add_numbers_spec.rb
         | 
| 188 191 | 
             
            - spec/acceptance/around_each_spec.rb
         | 
| 192 | 
            +
            - spec/acceptance/fail_spec.rb
         | 
| 189 193 | 
             
            - spec/acceptance/include_warning_spec.rb
         | 
| 190 194 | 
             
            - spec/acceptance/log_from_organizer_spec.rb
         | 
| 191 195 | 
             
            - spec/acceptance/message_localization_spec.rb
         | 
| @@ -196,6 +200,7 @@ test_files: | |
| 196 200 | 
             
            - spec/acceptance/orchestrator/organizer_action_combination_spec.rb
         | 
| 197 201 | 
             
            - spec/acceptance/orchestrator/reduce_if_spec.rb
         | 
| 198 202 | 
             
            - spec/acceptance/orchestrator/reduce_until_spec.rb
         | 
| 203 | 
            +
            - spec/acceptance/orchestrator/with_callback_spec.rb
         | 
| 199 204 | 
             
            - spec/acceptance/rollback_spec.rb
         | 
| 200 205 | 
             
            - spec/acceptance/skip_all_warning_spec.rb
         | 
| 201 206 | 
             
            - spec/acceptance/testing/context_factory_spec.rb
         | 
| @@ -203,6 +208,7 @@ test_files: | |
| 203 208 | 
             
            - spec/action_expects_and_promises_spec.rb
         | 
| 204 209 | 
             
            - spec/action_promised_keys_spec.rb
         | 
| 205 210 | 
             
            - spec/action_spec.rb
         | 
| 211 | 
            +
            - spec/context/inspect_spec.rb
         | 
| 206 212 | 
             
            - spec/context_spec.rb
         | 
| 207 213 | 
             
            - spec/localization_adapter_spec.rb
         | 
| 208 214 | 
             
            - spec/organizer/with_reducer_spec.rb
         |