light-service 0.10.1 → 0.13.0
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 +5 -5
- data/.gitignore +1 -0
- data/.rubocop.yml +6 -0
- data/.travis.yml +12 -10
- data/Appraisals +4 -0
- data/README.md +39 -20
- data/RELEASES.md +17 -0
- data/gemfiles/activesupport_6.gemfile +8 -0
- data/lib/light-service.rb +1 -0
- data/lib/light-service/context.rb +4 -1
- data/lib/light-service/localization_adapter.rb +1 -1
- data/lib/light-service/organizer.rb +32 -0
- data/lib/light-service/organizer/with_reducer.rb +4 -5
- data/lib/light-service/organizer/with_reducer_factory.rb +11 -7
- data/lib/light-service/organizer/with_reducer_log_decorator.rb +2 -2
- data/lib/light-service/testing/context_factory.rb +12 -7
- data/lib/light-service/version.rb +1 -1
- data/light-service.gemspec +5 -4
- data/spec/acceptance/after_actions_spec.rb +13 -0
- data/spec/acceptance/custom_log_from_organizer_spec.rb +60 -0
- data/spec/acceptance/fail_spec.rb +42 -16
- data/spec/acceptance/organizer/add_aliases_spec.rb +28 -0
- data/spec/acceptance/organizer/add_to_context_spec.rb +30 -0
- data/spec/acceptance/organizer/execute_spec.rb +1 -1
- data/spec/acceptance/organizer/reduce_if_spec.rb +32 -0
- data/spec/acceptance/testing/context_factory_spec.rb +12 -3
- data/spec/organizer_spec.rb +37 -14
- data/spec/sample/provides_free_shipping_action_spec.rb +1 -1
- data/spec/spec_helper.rb +2 -1
- data/spec/test_doubles.rb +93 -0
- data/spec/testing/context_factory_spec.rb +20 -0
- metadata +32 -15
- data/gemfiles/activesupport_3.gemfile.lock +0 -76
- data/gemfiles/activesupport_4.gemfile.lock +0 -82
- data/gemfiles/activesupport_5.gemfile.lock +0 -82
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 | 
            -
             | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 2 | 
            +
            SHA256:
         | 
| 3 | 
            +
              metadata.gz: 1e9dfb5cf61024afa28719b84e160c9580fa39c99eda19b73cdb0cced9017da2
         | 
| 4 | 
            +
              data.tar.gz: 7f389c208b3b06000c84859d595d9b49acf8dfe8d370736ee564a1ae8ef8b5d1
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: b1b7792d344efe1aab4297f8774649590b8e0336a44082da31a167a62b75a039b7f89759923beb9a463f6c017b0387297efc815f357deae31270b54e2c1c1bcc
         | 
| 7 | 
            +
              data.tar.gz: 0d1d339a23d2d7b39bd5403d81fe1e1519a89eec22fd64f00931d9dc49a81c5476f67e6dd28591e0e88c55b1378d1155ed0d1700100569aa9b43a1922c860f3e
         | 
    
        data/.gitignore
    CHANGED
    
    
    
        data/.rubocop.yml
    CHANGED
    
    | @@ -1,4 +1,7 @@ | |
| 1 | 
            +
            require: rubocop-performance
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            AllCops:
         | 
| 4 | 
            +
              TargetRubyVersion: 2.3
         | 
| 2 5 | 
             
              Exclude:
         | 
| 3 6 | 
             
                - 'lib/light-service.rb'
         | 
| 4 7 | 
             
                - 'vendor/bundle/**/*'
         | 
| @@ -46,3 +49,6 @@ Metrics/BlockLength: | |
| 46 49 |  | 
| 47 50 | 
             
            Layout/TrailingBlankLines:
         | 
| 48 51 | 
             
              Enabled: false
         | 
| 52 | 
            +
             | 
| 53 | 
            +
            Layout/EndOfLine:
         | 
| 54 | 
            +
              EnforcedStyle: lf
         | 
    
        data/.travis.yml
    CHANGED
    
    | @@ -4,16 +4,15 @@ env: | |
| 4 4 | 
             
              - RUN_COVERAGE_REPORT=true
         | 
| 5 5 |  | 
| 6 6 | 
             
            rvm:
         | 
| 7 | 
            -
              - 2. | 
| 8 | 
            -
              - 2. | 
| 9 | 
            -
              - 2. | 
| 10 | 
            -
              - 2. | 
| 7 | 
            +
              - 2.4.2
         | 
| 8 | 
            +
              - 2.5.3
         | 
| 9 | 
            +
              - 2.6.0
         | 
| 10 | 
            +
              - 2.7.0
         | 
| 11 11 |  | 
| 12 12 | 
             
            before_install:
         | 
| 13 13 | 
             
              - 'echo ''gem: --no-ri --no-rdoc'' > ~/.gemrc'
         | 
| 14 14 | 
             
              - gem install bundler
         | 
| 15 | 
            -
              - bundle  | 
| 16 | 
            -
              - bundle install --path vendor/bundle
         | 
| 15 | 
            +
              - bundle install --clean --path vendor/bundle
         | 
| 17 16 |  | 
| 18 17 | 
             
            # uncomment this line if your project needs to run something other than `rake`:
         | 
| 19 18 | 
             
            script:
         | 
| @@ -24,10 +23,13 @@ gemfile: | |
| 24 23 | 
             
              - gemfiles/activesupport_3.gemfile
         | 
| 25 24 | 
             
              - gemfiles/activesupport_4.gemfile
         | 
| 26 25 | 
             
              - gemfiles/activesupport_5.gemfile
         | 
| 26 | 
            +
              - gemfiles/activesupport_6.gemfile
         | 
| 27 27 |  | 
| 28 28 | 
             
            matrix:
         | 
| 29 29 | 
             
              exclude:
         | 
| 30 | 
            -
              - rvm: 2. | 
| 31 | 
            -
                gemfile: gemfiles/ | 
| 32 | 
            -
              - rvm: 2. | 
| 33 | 
            -
                gemfile: gemfiles/ | 
| 30 | 
            +
              - rvm: 2.4.2
         | 
| 31 | 
            +
                gemfile: gemfiles/activesupport_6.gemfile
         | 
| 32 | 
            +
              - rvm: 2.7.0
         | 
| 33 | 
            +
                gemfile: gemfiles/activesupport_3.gemfile
         | 
| 34 | 
            +
              - rvm: 2.7.0
         | 
| 35 | 
            +
                gemfile: gemfiles/activesupport_4.gemfile
         | 
    
        data/Appraisals
    CHANGED
    
    
    
        data/README.md
    CHANGED
    
    | @@ -1,10 +1,10 @@ | |
| 1 1 | 
             
            
         | 
| 2 2 |  | 
| 3 3 | 
             
            [](https://rubygems.org/gems/light-service)
         | 
| 4 | 
            -
            [](https://beta.gemnasium.com/projects/github.com/adomokos/light-service)
         | 
| 4 | 
            +
            [](http://travis-ci.org/adomokos/light-service)
         | 
| 5 | 
            +
            [](https://codeclimate.com/github/adomokos/light-service)
         | 
| 7 6 | 
             
            [](http://opensource.org/licenses/MIT)
         | 
| 7 | 
            +
            [](https://rubygems.org/gems/light-service)
         | 
| 8 8 |  | 
| 9 9 | 
             
            <br><br>
         | 
| 10 10 |  | 
| @@ -79,7 +79,7 @@ Wouldn't it be nice to see this instead? | |
| 79 79 | 
             
            (
         | 
| 80 80 | 
             
              LooksUpTaxPercentage,
         | 
| 81 81 | 
             
              CalculatesOrderTax,
         | 
| 82 | 
            -
               | 
| 82 | 
            +
              ProvidesFreeShipping
         | 
| 83 83 | 
             
            )
         | 
| 84 84 | 
             
            ```
         | 
| 85 85 |  | 
| @@ -257,12 +257,12 @@ Check out this example: | |
| 257 257 |  | 
| 258 258 | 
             
            ```ruby
         | 
| 259 259 | 
             
            class LogDuration
         | 
| 260 | 
            -
              def self.call( | 
| 260 | 
            +
              def self.call(context)
         | 
| 261 261 | 
             
                start_time = Time.now
         | 
| 262 262 | 
             
                result = yield
         | 
| 263 263 | 
             
                duration = Time.now - start_time
         | 
| 264 264 | 
             
                LightService::Configuration.logger.info(
         | 
| 265 | 
            -
                  :action   =>  | 
| 265 | 
            +
                  :action   => context.current_action,
         | 
| 266 266 | 
             
                  :duration => duration
         | 
| 267 267 | 
             
                )
         | 
| 268 268 |  | 
| @@ -295,14 +295,16 @@ Consider this code: | |
| 295 295 | 
             
            class SomeOrganizer
         | 
| 296 296 | 
             
              extend LightService::Organizer
         | 
| 297 297 |  | 
| 298 | 
            -
              def call(ctx)
         | 
| 298 | 
            +
              def self.call(ctx)
         | 
| 299 299 | 
             
                with(ctx).reduce(actions)
         | 
| 300 300 | 
             
              end
         | 
| 301 301 |  | 
| 302 | 
            -
              def actions
         | 
| 303 | 
            -
                 | 
| 304 | 
            -
             | 
| 305 | 
            -
             | 
| 302 | 
            +
              def self.actions
         | 
| 303 | 
            +
                [
         | 
| 304 | 
            +
                  OneAction,
         | 
| 305 | 
            +
                  TwoAction,
         | 
| 306 | 
            +
                  ThreeAction
         | 
| 307 | 
            +
                ]
         | 
| 306 308 | 
             
              end
         | 
| 307 309 | 
             
            end
         | 
| 308 310 |  | 
| @@ -346,14 +348,16 @@ class SomeOrganizer | |
| 346 348 | 
             
                                      end
         | 
| 347 349 | 
             
                                    end)
         | 
| 348 350 |  | 
| 349 | 
            -
              def call(ctx)
         | 
| 351 | 
            +
              def self.call(ctx)
         | 
| 350 352 | 
             
                with(ctx).reduce(actions)
         | 
| 351 353 | 
             
              end
         | 
| 352 354 |  | 
| 353 | 
            -
              def actions
         | 
| 354 | 
            -
                 | 
| 355 | 
            -
             | 
| 356 | 
            -
             | 
| 355 | 
            +
              def self.actions
         | 
| 356 | 
            +
                [
         | 
| 357 | 
            +
                  OneAction,
         | 
| 358 | 
            +
                  TwoAction,
         | 
| 359 | 
            +
                  ThreeAction
         | 
| 360 | 
            +
                ]
         | 
| 357 361 | 
             
              end
         | 
| 358 362 | 
             
            end
         | 
| 359 363 |  | 
| @@ -452,9 +456,9 @@ class AnOrganizer | |
| 452 456 |  | 
| 453 457 | 
             
              def self.call(order)
         | 
| 454 458 | 
             
                with(:order => order).reduce(
         | 
| 455 | 
            -
             | 
| 456 | 
            -
             | 
| 457 | 
            -
             | 
| 459 | 
            +
                  AnAction,
         | 
| 460 | 
            +
                  AnotherAction,
         | 
| 461 | 
            +
                )
         | 
| 458 462 | 
             
              end
         | 
| 459 463 | 
             
            end
         | 
| 460 464 |  | 
| @@ -535,6 +539,15 @@ I, [DATE]  INFO -- : [LightService] - ;-) <TestDoubles::MakesLatteAction> has de | |
| 535 539 | 
             
            I, [DATE]  INFO -- : [LightService] - context message: Can't make a latte with a fatty milk like that!
         | 
| 536 540 | 
             
            ```
         | 
| 537 541 |  | 
| 542 | 
            +
            You can specify the logger on the organizer level, so the organizer does not use the global logger.
         | 
| 543 | 
            +
             | 
| 544 | 
            +
            ```ruby
         | 
| 545 | 
            +
            class FooOrganizer
         | 
| 546 | 
            +
              extend LightService::Organizer
         | 
| 547 | 
            +
              log_with Logger.new("/my/special.log")
         | 
| 548 | 
            +
            end
         | 
| 549 | 
            +
            ```
         | 
| 550 | 
            +
             | 
| 538 551 | 
             
            ## Error Codes
         | 
| 539 552 | 
             
            You can add some more structure to your error handling by taking advantage of error codes in the context.
         | 
| 540 553 | 
             
            Normally, when something goes wrong in your actions, you fail the process by setting the context to failure:
         | 
| @@ -746,13 +759,15 @@ end | |
| 746 759 |  | 
| 747 760 | 
             
            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.
         | 
| 748 761 |  | 
| 749 | 
            -
            The  | 
| 762 | 
            +
            The 7 different orchestrator constructs an organizer can have:
         | 
| 750 763 |  | 
| 751 764 | 
             
            1. `reduce_until`
         | 
| 752 765 | 
             
            2. `reduce_if`
         | 
| 753 766 | 
             
            3. `iterate`
         | 
| 754 767 | 
             
            4. `execute`
         | 
| 755 768 | 
             
            5. `with_callback`
         | 
| 769 | 
            +
            6. `add_to_context`
         | 
| 770 | 
            +
            7. `add_aliases`
         | 
| 756 771 |  | 
| 757 772 | 
             
            `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/organizer/reduce_until_spec.rb) to see how it's used.
         | 
| 758 773 |  | 
| @@ -764,6 +779,10 @@ To take advantage of another organizer or action, you might need to tweak the co | |
| 764 779 |  | 
| 765 780 | 
             
            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/organizer/with_callback_spec.rb) as a working example.
         | 
| 766 781 |  | 
| 782 | 
            +
            `add_to_context` can add key-value pairs on the fly to the context. This functionality is useful when you need a value injected into the context under a specific key right before the subsequent actions are executed. [This test](spec/acceptance/organizer/add_to_context_spec.rb) describes its functionality.
         | 
| 783 | 
            +
             | 
| 784 | 
            +
            Your action needs a certain key in the context but it's under a different one? Use the function `add_aliases` to alias an existing key in the context under the desired key. Take a look at [this test](spec/acceptance/organizer/add_aliases_spec.rb) to see an example.
         | 
| 785 | 
            +
             | 
| 767 786 | 
             
            ## ContextFactory for Faster Action Testing
         | 
| 768 787 |  | 
| 769 788 | 
             
            As the complexity of your workflow increases, you will find yourself spending more and more time creating a context (LightService::Context it is) for your action tests. Some of this code can be reused by clever factories, but still, you are using a context that is artificial, and can be different from what the previous actions produced. This is especially true, when you use LightService in ETLs, where you start out with initial data and your actions are mutating its state.
         | 
    
        data/RELEASES.md
    CHANGED
    
    | @@ -1,5 +1,22 @@ | |
| 1 1 | 
             
            A brief list of new features and changes introduced with the specified version.
         | 
| 2 2 |  | 
| 3 | 
            +
            ### 0.13.0
         | 
| 4 | 
            +
            * [Add 'add_to_context' and 'add_aliases'](https://github.com/adomokos/light-service/pull/172)
         | 
| 5 | 
            +
            * Updating Ruby compatibility, minor fixes
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            ### 0.12.0
         | 
| 8 | 
            +
            * [Per organizer logger](https://github.com/adomokos/light-service/pull/162)
         | 
| 9 | 
            +
            * [Fix 'fail_and_return!' not accepting 'error_code' option](https://github.com/adomokos/light-service/pull/168)
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            ### 0.11.0
         | 
| 12 | 
            +
            * [Switch to 'each_with_object' in WithReducer](https://github.com/adomokos/light-service/pull/149).
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            ### 0.10.3
         | 
| 15 | 
            +
            * [Adding ContextFactory](https://github.com/adomokos/light-service/pull/147).
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            ### 0.10.2
         | 
| 18 | 
            +
            * [Revert 0.10.1](https://github.com/adomokos/light-service/pull/146), it breaks tests in our apps :-(.
         | 
| 19 | 
            +
             | 
| 3 20 | 
             
            ### 0.10.1
         | 
| 4 21 | 
             
            * [Fixing ContextFactory](https://github.com/adomokos/light-service/pull/141) for orchestrator methods in Organizers.
         | 
| 5 22 |  | 
    
        data/lib/light-service.rb
    CHANGED
    
    
| @@ -88,7 +88,7 @@ module LightService | |
| 88 88 |  | 
| 89 89 | 
             
                def fail_and_return!(*args)
         | 
| 90 90 | 
             
                  fail!(*args)
         | 
| 91 | 
            -
                  throw(:jump_when_failed | 
| 91 | 
            +
                  throw(:jump_when_failed)
         | 
| 92 92 | 
             
                end
         | 
| 93 93 |  | 
| 94 94 | 
             
                def fail_with_rollback!(message = nil, error_code = nil)
         | 
| @@ -115,8 +115,10 @@ module LightService | |
| 115 115 |  | 
| 116 116 | 
             
                def define_accessor_methods_for_keys(keys)
         | 
| 117 117 | 
             
                  return if keys.nil?
         | 
| 118 | 
            +
             | 
| 118 119 | 
             
                  keys.each do |key|
         | 
| 119 120 | 
             
                    next if respond_to?(key.to_sym)
         | 
| 121 | 
            +
             | 
| 120 122 | 
             
                    define_singleton_method(key.to_s) { fetch(key) }
         | 
| 121 123 | 
             
                    define_singleton_method("#{key}=") { |value| self[key] = value }
         | 
| 122 124 | 
             
                  end
         | 
| @@ -161,6 +163,7 @@ module LightService | |
| 161 163 |  | 
| 162 164 | 
             
                def check_nil(value)
         | 
| 163 165 | 
             
                  return 'nil' unless value
         | 
| 166 | 
            +
             | 
| 164 167 | 
             
                  "'#{value}'"
         | 
| 165 168 | 
             
                end
         | 
| 166 169 | 
             
              end
         | 
| @@ -56,6 +56,24 @@ module LightService | |
| 56 56 | 
             
                  def with_callback(action, steps)
         | 
| 57 57 | 
             
                    WithCallback.run(self, action, steps)
         | 
| 58 58 | 
             
                  end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                  def log_with(logger)
         | 
| 61 | 
            +
                    @logger = logger
         | 
| 62 | 
            +
                  end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                  def logger
         | 
| 65 | 
            +
                    @logger
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                  def add_to_context(**args)
         | 
| 69 | 
            +
                    args.map do |key, value|
         | 
| 70 | 
            +
                      execute(->(ctx) { ctx[key.to_sym] = value })
         | 
| 71 | 
            +
                    end
         | 
| 72 | 
            +
                  end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                  def add_aliases(args)
         | 
| 75 | 
            +
                    execute(->(ctx) { ctx.assign_aliases(ctx.aliases.merge(args)) })
         | 
| 76 | 
            +
                  end
         | 
| 59 77 | 
             
                end
         | 
| 60 78 |  | 
| 61 79 | 
             
                module Macros
         | 
| @@ -63,6 +81,8 @@ module LightService | |
| 63 81 | 
             
                    @aliases = key_hash
         | 
| 64 82 | 
             
                  end
         | 
| 65 83 |  | 
| 84 | 
            +
                  # This looks like an accessor,
         | 
| 85 | 
            +
                  # but it's used as a macro in the Organizer
         | 
| 66 86 | 
             
                  def before_actions(*logic)
         | 
| 67 87 | 
             
                    self.before_actions = logic
         | 
| 68 88 | 
             
                  end
         | 
| @@ -71,6 +91,13 @@ module LightService | |
| 71 91 | 
             
                    @before_actions = [logic].flatten
         | 
| 72 92 | 
             
                  end
         | 
| 73 93 |  | 
| 94 | 
            +
                  def append_before_actions(action)
         | 
| 95 | 
            +
                    @before_actions ||= []
         | 
| 96 | 
            +
                    @before_actions.push(action)
         | 
| 97 | 
            +
                  end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                  # This looks like an accessor,
         | 
| 100 | 
            +
                  # but it's used as a macro in the Organizer
         | 
| 74 101 | 
             
                  def after_actions(*logic)
         | 
| 75 102 | 
             
                    self.after_actions = logic
         | 
| 76 103 | 
             
                  end
         | 
| @@ -78,6 +105,11 @@ module LightService | |
| 78 105 | 
             
                  def after_actions=(logic)
         | 
| 79 106 | 
             
                    @after_actions = [logic].flatten
         | 
| 80 107 | 
             
                  end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                  def append_after_actions(action)
         | 
| 110 | 
            +
                    @after_actions ||= []
         | 
| 111 | 
            +
                    @after_actions.push(action)
         | 
| 112 | 
            +
                  end
         | 
| 81 113 | 
             
                end
         | 
| 82 114 | 
             
              end
         | 
| 83 115 | 
             
            end
         | 
| @@ -23,19 +23,18 @@ module LightService | |
| 23 23 |  | 
| 24 24 | 
             
                  def reduce(*actions)
         | 
| 25 25 | 
             
                    raise "No action(s) were provided" if actions.empty?
         | 
| 26 | 
            +
             | 
| 26 27 | 
             
                    actions.flatten!
         | 
| 27 28 |  | 
| 28 | 
            -
                    actions. | 
| 29 | 
            +
                    actions.each_with_object(context) do |action, current_context|
         | 
| 29 30 | 
             
                      begin
         | 
| 30 | 
            -
                         | 
| 31 | 
            +
                        invoke_action(current_context, action)
         | 
| 31 32 | 
             
                      rescue FailWithRollbackError
         | 
| 32 | 
            -
                         | 
| 33 | 
            +
                        reduce_rollback(actions)
         | 
| 33 34 | 
             
                      ensure
         | 
| 34 35 | 
             
                        # For logging
         | 
| 35 36 | 
             
                        yield(current_context, action) if block_given?
         | 
| 36 37 | 
             
                      end
         | 
| 37 | 
            -
             | 
| 38 | 
            -
                      result
         | 
| 39 38 | 
             
                    end
         | 
| 40 39 | 
             
                  end
         | 
| 41 40 |  | 
| @@ -2,13 +2,17 @@ module LightService | |
| 2 2 | 
             
              module Organizer
         | 
| 3 3 | 
             
                class WithReducerFactory
         | 
| 4 4 | 
             
                  def self.make(monitored_organizer)
         | 
| 5 | 
            -
                     | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
                     | 
| 10 | 
            -
             | 
| 11 | 
            -
                     | 
| 5 | 
            +
                    logger = monitored_organizer.logger ||
         | 
| 6 | 
            +
                             LightService::Configuration.logger
         | 
| 7 | 
            +
                    decorated = WithReducer.new
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                    return decorated if logger.nil?
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                    WithReducerLogDecorator.new(
         | 
| 12 | 
            +
                      monitored_organizer,
         | 
| 13 | 
            +
                      :decorated => decorated,
         | 
| 14 | 
            +
                      :logger => logger
         | 
| 15 | 
            +
                    )
         | 
| 12 16 | 
             
                  end
         | 
| 13 17 | 
             
                end
         | 
| 14 18 | 
             
              end
         | 
| @@ -5,10 +5,10 @@ module LightService | |
| 5 5 |  | 
| 6 6 | 
             
                  alias logged? logged
         | 
| 7 7 |  | 
| 8 | 
            -
                  def initialize(organizer, decorated  | 
| 8 | 
            +
                  def initialize(organizer, decorated: WithReducer.new, logger:)
         | 
| 9 9 | 
             
                    @decorated = decorated
         | 
| 10 10 | 
             
                    @organizer = organizer
         | 
| 11 | 
            -
                    @logger =  | 
| 11 | 
            +
                    @logger = logger
         | 
| 12 12 | 
             
                    @logged = false
         | 
| 13 13 | 
             
                  end
         | 
| 14 14 |  | 
| @@ -8,23 +8,28 @@ module LightService | |
| 8 8 | 
             
                  end
         | 
| 9 9 |  | 
| 10 10 | 
             
                  def for(action)
         | 
| 11 | 
            -
                    @organizer. | 
| 11 | 
            +
                    @organizer.append_before_actions(
         | 
| 12 12 | 
             
                      lambda do |ctx|
         | 
| 13 13 | 
             
                        if ctx.current_action == action
         | 
| 14 | 
            +
                          # The last `:_before_actions` hook is for
         | 
| 15 | 
            +
                          # ContextFactory, remove it, so it won't
         | 
| 16 | 
            +
                          # be invoked again
         | 
| 17 | 
            +
                          ctx[:_before_actions].pop
         | 
| 18 | 
            +
             | 
| 14 19 | 
             
                          throw(:return_ctx_from_execution, ctx)
         | 
| 15 20 | 
             
                        end
         | 
| 16 21 | 
             
                      end
         | 
| 17 | 
            -
                     | 
| 22 | 
            +
                    )
         | 
| 18 23 |  | 
| 19 24 | 
             
                    self
         | 
| 20 25 | 
             
                  end
         | 
| 21 26 |  | 
| 22 | 
            -
                   | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 27 | 
            +
                  # More than one arguments can be passed to the
         | 
| 28 | 
            +
                  # Organizer's #call method
         | 
| 29 | 
            +
                  def with(*args, &block)
         | 
| 30 | 
            +
                    catch(:return_ctx_from_execution) do
         | 
| 31 | 
            +
                      @organizer.call(*args, &block)
         | 
| 25 32 | 
             
                    end
         | 
| 26 | 
            -
             | 
| 27 | 
            -
                    escaped
         | 
| 28 33 | 
             
                  end
         | 
| 29 34 |  | 
| 30 35 | 
             
                  def initialize(organizer)
         |