waterfall 1.0.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 +7 -0
 - data/.gitignore +34 -0
 - data/.rspec +2 -0
 - data/.travis.yml +8 -0
 - data/Gemfile +6 -0
 - data/Gemfile.lock +53 -0
 - data/LICENSE.txt +22 -0
 - data/README.md +345 -0
 - data/lib/waterfall.rb +85 -0
 - data/lib/waterfall/predicates/base.rb +23 -0
 - data/lib/waterfall/predicates/chain.rb +26 -0
 - data/lib/waterfall/predicates/on_dam.rb +13 -0
 - data/lib/waterfall/predicates/when_falsy.rb +19 -0
 - data/lib/waterfall/predicates/when_truthy.rb +19 -0
 - data/lib/waterfall/version.rb +3 -0
 - data/spec/integration_spec.rb +197 -0
 - data/spec/spec_helper.rb +23 -0
 - data/waterfall.gemspec +26 -0
 - metadata +135 -0
 
    
        checksums.yaml
    ADDED
    
    | 
         @@ -0,0 +1,7 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            ---
         
     | 
| 
      
 2 
     | 
    
         
            +
            SHA1:
         
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 129bb57a29df2394c12511053c406490b8a99674
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: e7260b30ce91910cdb8d1466a39898000bde0fa5
         
     | 
| 
      
 5 
     | 
    
         
            +
            SHA512:
         
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 80c9da8ab97a26854f8fa5700e1a8669409f2212fe16660805dff074894df2ba89aea2bec52efd232b874646de286cc992f9ad8d3be679fd63beefffdaa132fd
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: c9253aa6ce5ce77577398006957eab695b8489db2b914adfb68e44b0319820d6e8fa92159ed6a77aed93726f803e476194e7227bb8a844f82adf7283de6ddd9a
         
     | 
    
        data/.gitignore
    ADDED
    
    | 
         @@ -0,0 +1,34 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            *.gem
         
     | 
| 
      
 2 
     | 
    
         
            +
            *.rbc
         
     | 
| 
      
 3 
     | 
    
         
            +
            /.config
         
     | 
| 
      
 4 
     | 
    
         
            +
            /coverage/
         
     | 
| 
      
 5 
     | 
    
         
            +
            /InstalledFiles
         
     | 
| 
      
 6 
     | 
    
         
            +
            /pkg/
         
     | 
| 
      
 7 
     | 
    
         
            +
            /spec/reports/
         
     | 
| 
      
 8 
     | 
    
         
            +
            /test/tmp/
         
     | 
| 
      
 9 
     | 
    
         
            +
            /test/version_tmp/
         
     | 
| 
      
 10 
     | 
    
         
            +
            /tmp/
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            ## Specific to RubyMotion:
         
     | 
| 
      
 13 
     | 
    
         
            +
            .dat*
         
     | 
| 
      
 14 
     | 
    
         
            +
            .repl_history
         
     | 
| 
      
 15 
     | 
    
         
            +
            build/
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            ## Documentation cache and generated files:
         
     | 
| 
      
 18 
     | 
    
         
            +
            /.yardoc/
         
     | 
| 
      
 19 
     | 
    
         
            +
            /_yardoc/
         
     | 
| 
      
 20 
     | 
    
         
            +
            /doc/
         
     | 
| 
      
 21 
     | 
    
         
            +
            /rdoc/
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
            ## Environment normalisation:
         
     | 
| 
      
 24 
     | 
    
         
            +
            /.bundle/
         
     | 
| 
      
 25 
     | 
    
         
            +
            /lib/bundler/man/
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
            # for a library or gem, you might want to ignore these files since the code is
         
     | 
| 
      
 28 
     | 
    
         
            +
            # intended to run in multiple environments; otherwise, check them in:
         
     | 
| 
      
 29 
     | 
    
         
            +
            # Gemfile.lock
         
     | 
| 
      
 30 
     | 
    
         
            +
            # .ruby-version
         
     | 
| 
      
 31 
     | 
    
         
            +
            # .ruby-gemset
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
            # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
         
     | 
| 
      
 34 
     | 
    
         
            +
            .rvmrc
         
     | 
    
        data/.rspec
    ADDED
    
    
    
        data/.travis.yml
    ADDED
    
    
    
        data/Gemfile
    ADDED
    
    
    
        data/Gemfile.lock
    ADDED
    
    | 
         @@ -0,0 +1,53 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            PATH
         
     | 
| 
      
 2 
     | 
    
         
            +
              remote: .
         
     | 
| 
      
 3 
     | 
    
         
            +
              specs:
         
     | 
| 
      
 4 
     | 
    
         
            +
                waterfall (1.0.2)
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            GEM
         
     | 
| 
      
 7 
     | 
    
         
            +
              remote: https://rubygems.org/
         
     | 
| 
      
 8 
     | 
    
         
            +
              specs:
         
     | 
| 
      
 9 
     | 
    
         
            +
                codeclimate-test-reporter (0.4.7)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  simplecov (>= 0.7.1, < 1.0.0)
         
     | 
| 
      
 11 
     | 
    
         
            +
                coderay (1.1.0)
         
     | 
| 
      
 12 
     | 
    
         
            +
                diff-lcs (1.2.5)
         
     | 
| 
      
 13 
     | 
    
         
            +
                docile (1.1.5)
         
     | 
| 
      
 14 
     | 
    
         
            +
                json (1.8.2)
         
     | 
| 
      
 15 
     | 
    
         
            +
                method_source (0.8.2)
         
     | 
| 
      
 16 
     | 
    
         
            +
                pry (0.10.1)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  coderay (~> 1.1.0)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  method_source (~> 0.8.1)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  slop (~> 3.4)
         
     | 
| 
      
 20 
     | 
    
         
            +
                pry-nav (0.2.4)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  pry (>= 0.9.10, < 0.11.0)
         
     | 
| 
      
 22 
     | 
    
         
            +
                rake (10.3.2)
         
     | 
| 
      
 23 
     | 
    
         
            +
                rspec (3.2.0)
         
     | 
| 
      
 24 
     | 
    
         
            +
                  rspec-core (~> 3.2.0)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  rspec-expectations (~> 3.2.0)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  rspec-mocks (~> 3.2.0)
         
     | 
| 
      
 27 
     | 
    
         
            +
                rspec-core (3.2.3)
         
     | 
| 
      
 28 
     | 
    
         
            +
                  rspec-support (~> 3.2.0)
         
     | 
| 
      
 29 
     | 
    
         
            +
                rspec-expectations (3.2.1)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  diff-lcs (>= 1.2.0, < 2.0)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  rspec-support (~> 3.2.0)
         
     | 
| 
      
 32 
     | 
    
         
            +
                rspec-mocks (3.2.1)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  diff-lcs (>= 1.2.0, < 2.0)
         
     | 
| 
      
 34 
     | 
    
         
            +
                  rspec-support (~> 3.2.0)
         
     | 
| 
      
 35 
     | 
    
         
            +
                rspec-support (3.2.2)
         
     | 
| 
      
 36 
     | 
    
         
            +
                simplecov (0.10.0)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  docile (~> 1.1.0)
         
     | 
| 
      
 38 
     | 
    
         
            +
                  json (~> 1.8)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  simplecov-html (~> 0.10.0)
         
     | 
| 
      
 40 
     | 
    
         
            +
                simplecov-html (0.10.0)
         
     | 
| 
      
 41 
     | 
    
         
            +
                slop (3.6.0)
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
            PLATFORMS
         
     | 
| 
      
 44 
     | 
    
         
            +
              ruby
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
            DEPENDENCIES
         
     | 
| 
      
 47 
     | 
    
         
            +
              bundler (~> 1.3)
         
     | 
| 
      
 48 
     | 
    
         
            +
              codeclimate-test-reporter
         
     | 
| 
      
 49 
     | 
    
         
            +
              pry (> 0.10)
         
     | 
| 
      
 50 
     | 
    
         
            +
              pry-nav
         
     | 
| 
      
 51 
     | 
    
         
            +
              rake
         
     | 
| 
      
 52 
     | 
    
         
            +
              rspec (= 3.2)
         
     | 
| 
      
 53 
     | 
    
         
            +
              waterfall!
         
     | 
    
        data/LICENSE.txt
    ADDED
    
    | 
         @@ -0,0 +1,22 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            Copyright (c) 2014 Benjamin Roth
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            MIT License
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            Permission is hereby granted, free of charge, to any person obtaining
         
     | 
| 
      
 6 
     | 
    
         
            +
            a copy of this software and associated documentation files (the
         
     | 
| 
      
 7 
     | 
    
         
            +
            "Software"), to deal in the Software without restriction, including
         
     | 
| 
      
 8 
     | 
    
         
            +
            without limitation the rights to use, copy, modify, merge, publish,
         
     | 
| 
      
 9 
     | 
    
         
            +
            distribute, sublicense, and/or sell copies of the Software, and to
         
     | 
| 
      
 10 
     | 
    
         
            +
            permit persons to whom the Software is furnished to do so, subject to
         
     | 
| 
      
 11 
     | 
    
         
            +
            the following conditions:
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            The above copyright notice and this permission notice shall be
         
     | 
| 
      
 14 
     | 
    
         
            +
            included in all copies or substantial portions of the Software.
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
         
     | 
| 
      
 17 
     | 
    
         
            +
            EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
         
     | 
| 
      
 18 
     | 
    
         
            +
            MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
         
     | 
| 
      
 19 
     | 
    
         
            +
            NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
         
     | 
| 
      
 20 
     | 
    
         
            +
            LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
         
     | 
| 
      
 21 
     | 
    
         
            +
            OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
         
     | 
| 
      
 22 
     | 
    
         
            +
            WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         
     | 
    
        data/README.md
    ADDED
    
    | 
         @@ -0,0 +1,345 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            
         
     | 
| 
      
 2 
     | 
    
         
            +
            [](https://codeclimate.com/github/apneadiving/waterfall)
         
     | 
| 
      
 3 
     | 
    
         
            +
            [](https://codeclimate.com/github/apneadiving/waterfall/coverage)
         
     | 
| 
      
 4 
     | 
    
         
            +
            [](https://travis-ci.org/apneadiving/waterfall)
         
     | 
| 
      
 5 
     | 
    
         
            +
            #### Goal
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            Be able to chain ruby commands, and treat them like a flow.
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            General presentation slides can [be found here](https://slides.com/apneadiving/code-ruby-like-you-build-legos).
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            Check [the slides here](https://slides.com/apneadiving/handling-error-in-ruby-rails) for a refactoring example.
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            #### Basic example
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 17 
     | 
    
         
            +
            Wf.new
         
     | 
| 
      
 18 
     | 
    
         
            +
              .when_falsy { @user.update(user_params) }
         
     | 
| 
      
 19 
     | 
    
         
            +
                .dam { @user.errors }
         
     | 
| 
      
 20 
     | 
    
         
            +
              .chain { render json: @user }
         
     | 
| 
      
 21 
     | 
    
         
            +
              .on_dam { |errors| render json: { errors: errors.full_messages }, status: 422 }
         
     | 
| 
      
 22 
     | 
    
         
            +
            ```
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            When logic is complicated, waterfalls show their true power and let you write intention revealing code. Above all they excel at chaining services.
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            #### Rationale
         
     | 
| 
      
 27 
     | 
    
         
            +
            Coding is all about writing a flow of commands.
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
            Generally you basically go on, unless something wrong happens. Whenever this happens you have to halt the flow and send feedback to the user.
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
            When conditions stack up, readability decreases.
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
            One way to solve it is to create abstractions to wrap your business logic (service objects or the like). There some questions arise:
         
     | 
| 
      
 34 
     | 
    
         
            +
            * what should a good service return?
         
     | 
| 
      
 35 
     | 
    
         
            +
            * how to handle errors?
         
     | 
| 
      
 36 
     | 
    
         
            +
            * how to call a service within a service?
         
     | 
| 
      
 37 
     | 
    
         
            +
            * how to chain services / commands
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
            Those topics are discussed in [the slides here](https://slides.com/apneadiving/service-objects-waterfall-rails).
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
            ## Wf object
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
            The `Wf` class just includes the `Waterfall` module. It makes it easy to create standalone waterfalls mostly to chain actions or to chain services including `Waterfall` or returning a `Wf` object.
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
            Basically `chain` statements are executed in the order they appear. But if ever the waterfall is dammed, they are skipped.
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
            If a main waterfall chains another waterfall and the child waterfall is dammed, the main waterfall would be dammed.
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
            The point is to be able to be able to chain an expected set of actions whenever everything works fine. And to be able to quickly stop and get the errors back whenever something wrong happens.
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
            ## Installation
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
            There exists a gem on rubygem with the same name but its not mine :)
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
            For installation:
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                gem 'waterfall', git: 'git://github.com/apneadiving/waterfall.git'
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
            ## Waterfall mixin
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
            ### Overview
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
            The following are equivalent:
         
     | 
| 
      
 66 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 67 
     | 
    
         
            +
            # 1
         
     | 
| 
      
 68 
     | 
    
         
            +
            Wf.new.chain{ 1 + 1 }
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
            # 2
         
     | 
| 
      
 71 
     | 
    
         
            +
            class MyService
         
     | 
| 
      
 72 
     | 
    
         
            +
              include Waterfall
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
              def call
         
     | 
| 
      
 75 
     | 
    
         
            +
                self.chain{ 1 + 1 }
         
     | 
| 
      
 76 
     | 
    
         
            +
              end
         
     | 
| 
      
 77 
     | 
    
         
            +
            end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
            MyService.new.call
         
     | 
| 
      
 80 
     | 
    
         
            +
            ```
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
            This illustrates one convention classes including the mixin should obey: respond to `call`
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
            ### Outputs
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
            Each waterfall has its own `outflow` and `error_pool`.
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
            `outflow` is an Openstruct so you can get/set its property like a hash or like a standard object.
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
            For the `error_pool`, its up to you. But using Rails, I usually `include ActiveModel::Validations` in my services.
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
            Thus you:
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
            * have a standard way to deal with errors
         
     | 
| 
      
 95 
     | 
    
         
            +
            * can deal with multiple errors
         
     | 
| 
      
 96 
     | 
    
         
            +
            * support I18n out of the box
         
     | 
| 
      
 97 
     | 
    
         
            +
            * can use your model errors out of the box
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
            ## Illustration of chaining
         
     | 
| 
      
 100 
     | 
    
         
            +
            Doing
         
     | 
| 
      
 101 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 102 
     | 
    
         
            +
             Wf.new
         
     | 
| 
      
 103 
     | 
    
         
            +
               .chain(foo: :bar) { Wf.new.chain(:bar){ 1 } }
         
     | 
| 
      
 104 
     | 
    
         
            +
            ```
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
            is the same as doing:
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 109 
     | 
    
         
            +
             Wf.new
         
     | 
| 
      
 110 
     | 
    
         
            +
               .chain do |outflow, parent_waterfall|
         
     | 
| 
      
 111 
     | 
    
         
            +
                 unless parent_waterfall.dammed?
         
     | 
| 
      
 112 
     | 
    
         
            +
                   child = Wf.new.chain(:bar){ 1 }
         
     | 
| 
      
 113 
     | 
    
         
            +
                   if child.dammed?
         
     | 
| 
      
 114 
     | 
    
         
            +
                     parent_waterfall.dam(child.error_pool)
         
     | 
| 
      
 115 
     | 
    
         
            +
                   else
         
     | 
| 
      
 116 
     | 
    
         
            +
                     parent_waterfall.ouflow.foo = child.outflow.bar
         
     | 
| 
      
 117 
     | 
    
         
            +
                   end
         
     | 
| 
      
 118 
     | 
    
         
            +
                 end
         
     | 
| 
      
 119 
     | 
    
         
            +
               end
         
     | 
| 
      
 120 
     | 
    
         
            +
            ```
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
            Hopefully you better get the chaining power this way.
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
            ## Predicates
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
            ### chain(name_or_mapping = nil, &block) | block signature: (outflow, waterfall)
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
            Chain is the main predicate, what it does depends on what the block returns
         
     | 
| 
      
 129 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 130 
     | 
    
         
            +
             # main waterfall
         
     | 
| 
      
 131 
     | 
    
         
            +
             Wf.new
         
     | 
| 
      
 132 
     | 
    
         
            +
               .chain(foo: :bar) do
         
     | 
| 
      
 133 
     | 
    
         
            +
                 # child waterfall
         
     | 
| 
      
 134 
     | 
    
         
            +
                 Wf.new.chain(:bar){ 1 }.chain(:baz){ 2 }.chain{ 3 }
         
     | 
| 
      
 135 
     | 
    
         
            +
               end
         
     | 
| 
      
 136 
     | 
    
         
            +
            ```
         
     | 
| 
      
 137 
     | 
    
         
            +
            ##### when block doesn't return a waterfall
         
     | 
| 
      
 138 
     | 
    
         
            +
             
     | 
| 
      
 139 
     | 
    
         
            +
            The child waterfall would have the following outflow: `{ bar: 1, baz: 2 }`
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
            This illustrates that when the block returns a value which is not a waterfall, it stores the returned value of the block inside the `name_or_mapping` key of the `outflow` or doesn't store it if `name_or_mapping` is `nil`.
         
     | 
| 
      
 142 
     | 
    
         
            +
             
     | 
| 
      
 143 
     | 
    
         
            +
            Be aware those are equivalent:
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 146 
     | 
    
         
            +
            Wf.new.chain(:foo) { 1 }
         
     | 
| 
      
 147 
     | 
    
         
            +
            Wf.new.chain{|outflow| outflow[:foo] = 1 }
         
     | 
| 
      
 148 
     | 
    
         
            +
            Wf.new.chain{|outflow| outflow.foo = 1 }
         
     | 
| 
      
 149 
     | 
    
         
            +
            Wf.new.chain{|outflow, waterfall| waterfall.update_outflow(:foo, 1) }
         
     | 
| 
      
 150 
     | 
    
         
            +
            Wf.new.chain{|outflow, waterfall| waterfall.outflow.foo = 1 }
         
     | 
| 
      
 151 
     | 
    
         
            +
            ```
         
     | 
| 
      
 152 
     | 
    
         
            +
            ##### when block returns a waterfall
         
     | 
| 
      
 153 
     | 
    
         
            +
             
     | 
| 
      
 154 
     | 
    
         
            +
            The main waterfall would have the following outflow: `{ foo: 1 }`
         
     | 
| 
      
 155 
     | 
    
         
            +
             
     | 
| 
      
 156 
     | 
    
         
            +
            The main waterfall above receives the child waterfall as a return value of its `chain` block.
         
     | 
| 
      
 157 
     | 
    
         
            +
            All waterfalls have independent outflows.
         
     | 
| 
      
 158 
     | 
    
         
            +
             
     | 
| 
      
 159 
     | 
    
         
            +
            If `name_or_mapping` is `nil`, the main waterfall's `outflow` wouldnt be affected by its child (but if the child is dammed, the parent will be dammed).
         
     | 
| 
      
 160 
     | 
    
         
            +
             
     | 
| 
      
 161 
     | 
    
         
            +
            If `name_or_mapping` is a `hash`, the format must be read as `{ name_in_parent_waterfall: :name_from_child_waterfall}`. In the above example, the child returned an `outflow` with a `bar` key which has be renamed as `foo` in the main one.
         
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
            It may look useless, because most of the time you may not rename, but... It makes things clear. You know exactly what you expect and you know exactly that you dont expect the rest the child may provide.
         
     | 
| 
      
 164 
     | 
    
         
            +
             
     | 
| 
      
 165 
     | 
    
         
            +
            ### when_falsy(&block) | block signature: (error_pool, waterfall)
         
     | 
| 
      
 166 
     | 
    
         
            +
             
     | 
| 
      
 167 
     | 
    
         
            +
            This predicate must ***always*** be used followed with `dam` like:
         
     | 
| 
      
 168 
     | 
    
         
            +
             
     | 
| 
      
 169 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 170 
     | 
    
         
            +
            Wf.new
         
     | 
| 
      
 171 
     | 
    
         
            +
              .chain(:foo) { 1 }
         
     | 
| 
      
 172 
     | 
    
         
            +
              .when_falsy { true }
         
     | 
| 
      
 173 
     | 
    
         
            +
               .dam { "this wouldnt be executed"  }
         
     | 
| 
      
 174 
     | 
    
         
            +
              .when_falsy { false }
         
     | 
| 
      
 175 
     | 
    
         
            +
               .dam { "errrrr"  }
         
     | 
| 
      
 176 
     | 
    
         
            +
              .chain(:bar) { 2 }
         
     | 
| 
      
 177 
     | 
    
         
            +
              .on_dam {|error_pool| puts error_pool  }
         
     | 
| 
      
 178 
     | 
    
         
            +
            ```
         
     | 
| 
      
 179 
     | 
    
         
            +
             
     | 
| 
      
 180 
     | 
    
         
            +
            If the block returns a falsy value, it executes the `dam` block, which will store the returned value in the `error_pool`.
         
     | 
| 
      
 181 
     | 
    
         
            +
             
     | 
| 
      
 182 
     | 
    
         
            +
            Once the waterfall is dammed, all following `chain` blocks are skipped (wont be executed). And all the following `on_dam` block would be executed.
         
     | 
| 
      
 183 
     | 
    
         
            +
             
     | 
| 
      
 184 
     | 
    
         
            +
            As a result the example above would return a waterfall object having its `outflow` equal to `{ foo: 1 }`. Remember: it has been dammed before `bar` would have been set.
         
     | 
| 
      
 185 
     | 
    
         
            +
             
     | 
| 
      
 186 
     | 
    
         
            +
            Its `error_pool` would be `"errrrr"` and it would be `puts` as a result of the `on_dam`
         
     | 
| 
      
 187 
     | 
    
         
            +
             
     | 
| 
      
 188 
     | 
    
         
            +
            Be aware those are equivalent:
         
     | 
| 
      
 189 
     | 
    
         
            +
             
     | 
| 
      
 190 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 191 
     | 
    
         
            +
            Wf.new.when_falsy{ false }.dam{ 'errrr' }
         
     | 
| 
      
 192 
     | 
    
         
            +
            Wf.new.chain{ |outflow, waterfall| waterfall.dam('errrr') unless false }
         
     | 
| 
      
 193 
     | 
    
         
            +
            ```
         
     | 
| 
      
 194 
     | 
    
         
            +
             
     | 
| 
      
 195 
     | 
    
         
            +
            ### when_truthy(&block) | block signature: (error_pool, waterfall)
         
     | 
| 
      
 196 
     | 
    
         
            +
             
     | 
| 
      
 197 
     | 
    
         
            +
            Behaves the same as `when_falsy` except it dams when its return value is truthy
         
     | 
| 
      
 198 
     | 
    
         
            +
             
     | 
| 
      
 199 
     | 
    
         
            +
            ## Syntactic sugar
         
     | 
| 
      
 200 
     | 
    
         
            +
            Given:
         
     | 
| 
      
 201 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 202 
     | 
    
         
            +
            class MyWaterfall
         
     | 
| 
      
 203 
     | 
    
         
            +
              include Waterfall
         
     | 
| 
      
 204 
     | 
    
         
            +
              def call
         
     | 
| 
      
 205 
     | 
    
         
            +
                self.chain { 1 }
         
     | 
| 
      
 206 
     | 
    
         
            +
              end
         
     | 
| 
      
 207 
     | 
    
         
            +
            end
         
     | 
| 
      
 208 
     | 
    
         
            +
            ```
         
     | 
| 
      
 209 
     | 
    
         
            +
            You may have noticed that I usually write:
         
     | 
| 
      
 210 
     | 
    
         
            +
             
     | 
| 
      
 211 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 212 
     | 
    
         
            +
            Wf.new
         
     | 
| 
      
 213 
     | 
    
         
            +
              .chain { MyWaterfall.new }
         
     | 
| 
      
 214 
     | 
    
         
            +
            ```
         
     | 
| 
      
 215 
     | 
    
         
            +
            instead of
         
     | 
| 
      
 216 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 217 
     | 
    
         
            +
            Wf.new
         
     | 
| 
      
 218 
     | 
    
         
            +
              .chain { MyWaterfall.new.call }
         
     | 
| 
      
 219 
     | 
    
         
            +
            ```
         
     | 
| 
      
 220 
     | 
    
         
            +
            Both are the same: if a block returns a waterfall which was not executed, it will execute it (hence the `call` convention)
         
     | 
| 
      
 221 
     | 
    
         
            +
             
     | 
| 
      
 222 
     | 
    
         
            +
            ### on_dam(&block) | block signature: (error_pool, outflow, waterfall)
         
     | 
| 
      
 223 
     | 
    
         
            +
             
     | 
| 
      
 224 
     | 
    
         
            +
            Its block is executed whenever the waterfall is dammed, skipped otherwise.
         
     | 
| 
      
 225 
     | 
    
         
            +
             
     | 
| 
      
 226 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 227 
     | 
    
         
            +
            Wf.new
         
     | 
| 
      
 228 
     | 
    
         
            +
              .when_falsy { false }
         
     | 
| 
      
 229 
     | 
    
         
            +
              .on_dam {|error_pool, outflow, waterfall| puts error_pool  }
         
     | 
| 
      
 230 
     | 
    
         
            +
            ```
         
     | 
| 
      
 231 
     | 
    
         
            +
             
     | 
| 
      
 232 
     | 
    
         
            +
            ## Error propagation
         
     | 
| 
      
 233 
     | 
    
         
            +
             
     | 
| 
      
 234 
     | 
    
         
            +
            Whenever a a waterfall is dammed, all the following chains are skipped.
         
     | 
| 
      
 235 
     | 
    
         
            +
             
     | 
| 
      
 236 
     | 
    
         
            +
            * all the following chains are skipped
         
     | 
| 
      
 237 
     | 
    
         
            +
            * all `on_dam` blocks are executed
         
     | 
| 
      
 238 
     | 
    
         
            +
             
     | 
| 
      
 239 
     | 
    
         
            +
            ## Testing a Waterfall service
         
     | 
| 
      
 240 
     | 
    
         
            +
             
     | 
| 
      
 241 
     | 
    
         
            +
            Say I have this service:
         
     | 
| 
      
 242 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 243 
     | 
    
         
            +
            class AuthenticateUser
         
     | 
| 
      
 244 
     | 
    
         
            +
              include Waterfall
         
     | 
| 
      
 245 
     | 
    
         
            +
              include ActiveModel::Validations
         
     | 
| 
      
 246 
     | 
    
         
            +
             
     | 
| 
      
 247 
     | 
    
         
            +
              validates :user, presence: true
         
     | 
| 
      
 248 
     | 
    
         
            +
              attr_reader :user
         
     | 
| 
      
 249 
     | 
    
         
            +
             
     | 
| 
      
 250 
     | 
    
         
            +
              def initialize(email, password)
         
     | 
| 
      
 251 
     | 
    
         
            +
                @email, @password = email, @password
         
     | 
| 
      
 252 
     | 
    
         
            +
              end
         
     | 
| 
      
 253 
     | 
    
         
            +
             
     | 
| 
      
 254 
     | 
    
         
            +
              def call
         
     | 
| 
      
 255 
     | 
    
         
            +
                self
         
     | 
| 
      
 256 
     | 
    
         
            +
                  .chain { @user = User.authenticate(@email, @password) }
         
     | 
| 
      
 257 
     | 
    
         
            +
                  .when_falsy { valid? }
         
     | 
| 
      
 258 
     | 
    
         
            +
                     .dam { errors }
         
     | 
| 
      
 259 
     | 
    
         
            +
                  .chain(:user) { user }
         
     | 
| 
      
 260 
     | 
    
         
            +
              end
         
     | 
| 
      
 261 
     | 
    
         
            +
            end
         
     | 
| 
      
 262 
     | 
    
         
            +
            ```
         
     | 
| 
      
 263 
     | 
    
         
            +
            I could spec it this way:
         
     | 
| 
      
 264 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 265 
     | 
    
         
            +
            describe AuthenticateUser do
         
     | 
| 
      
 266 
     | 
    
         
            +
              let(:email)    { 'email@email.com' }
         
     | 
| 
      
 267 
     | 
    
         
            +
              let(:password) { 'password' }
         
     | 
| 
      
 268 
     | 
    
         
            +
              subject(:service) { AuthenticateUser.new(email, password).call }
         
     | 
| 
      
 269 
     | 
    
         
            +
             
     | 
| 
      
 270 
     | 
    
         
            +
              context "when given valid credentials" do
         
     | 
| 
      
 271 
     | 
    
         
            +
                let(:user) { double(:user) }
         
     | 
| 
      
 272 
     | 
    
         
            +
             
     | 
| 
      
 273 
     | 
    
         
            +
                before do
         
     | 
| 
      
 274 
     | 
    
         
            +
                  allow(User).to receive(:authenticate).with(email, password).and_return(user)
         
     | 
| 
      
 275 
     | 
    
         
            +
                end
         
     | 
| 
      
 276 
     | 
    
         
            +
             
     | 
| 
      
 277 
     | 
    
         
            +
                it "succeeds" do
         
     | 
| 
      
 278 
     | 
    
         
            +
                  expect(service.dammed?).to be false
         
     | 
| 
      
 279 
     | 
    
         
            +
                end
         
     | 
| 
      
 280 
     | 
    
         
            +
             
     | 
| 
      
 281 
     | 
    
         
            +
                it "provides the user" do
         
     | 
| 
      
 282 
     | 
    
         
            +
                  expect(service.outflow.user).to eq(user)
         
     | 
| 
      
 283 
     | 
    
         
            +
                end
         
     | 
| 
      
 284 
     | 
    
         
            +
              end
         
     | 
| 
      
 285 
     | 
    
         
            +
             
     | 
| 
      
 286 
     | 
    
         
            +
              context "when given invalid credentials" do
         
     | 
| 
      
 287 
     | 
    
         
            +
                before do
         
     | 
| 
      
 288 
     | 
    
         
            +
                  allow(User).to receive(:authenticate).with(email, password).and_return(nil)
         
     | 
| 
      
 289 
     | 
    
         
            +
                end
         
     | 
| 
      
 290 
     | 
    
         
            +
             
     | 
| 
      
 291 
     | 
    
         
            +
                it "fails" do
         
     | 
| 
      
 292 
     | 
    
         
            +
                  expect(service.dammed?).to be true
         
     | 
| 
      
 293 
     | 
    
         
            +
                end
         
     | 
| 
      
 294 
     | 
    
         
            +
             
     | 
| 
      
 295 
     | 
    
         
            +
                it "provides a failure message" do
         
     | 
| 
      
 296 
     | 
    
         
            +
                  expect(service.error_pool).to be_present
         
     | 
| 
      
 297 
     | 
    
         
            +
                end
         
     | 
| 
      
 298 
     | 
    
         
            +
              end
         
     | 
| 
      
 299 
     | 
    
         
            +
            end
         
     | 
| 
      
 300 
     | 
    
         
            +
            ```
         
     | 
| 
      
 301 
     | 
    
         
            +
            Syntax advice
         
     | 
| 
      
 302 
     | 
    
         
            +
            =========
         
     | 
| 
      
 303 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 304 
     | 
    
         
            +
            # this is valid
         
     | 
| 
      
 305 
     | 
    
         
            +
            self
         
     | 
| 
      
 306 
     | 
    
         
            +
              .chain { Service1.new }
         
     | 
| 
      
 307 
     | 
    
         
            +
              .chain { Service2.new }
         
     | 
| 
      
 308 
     | 
    
         
            +
             
     | 
| 
      
 309 
     | 
    
         
            +
            # this is equivalent
         
     | 
| 
      
 310 
     | 
    
         
            +
            self.chain { Service1.new }
         
     | 
| 
      
 311 
     | 
    
         
            +
            self.chain { Service2.new }
         
     | 
| 
      
 312 
     | 
    
         
            +
             
     | 
| 
      
 313 
     | 
    
         
            +
            # this is equivalent too
         
     | 
| 
      
 314 
     | 
    
         
            +
            chain { Service1.new }
         
     | 
| 
      
 315 
     | 
    
         
            +
            chain { Service2.new }
         
     | 
| 
      
 316 
     | 
    
         
            +
             
     | 
| 
      
 317 
     | 
    
         
            +
            # this is invalid Ruby due to the extra line
         
     | 
| 
      
 318 
     | 
    
         
            +
            self
         
     | 
| 
      
 319 
     | 
    
         
            +
              .chain { Service1.new }
         
     | 
| 
      
 320 
     | 
    
         
            +
             
     | 
| 
      
 321 
     | 
    
         
            +
              .chain { Service2.new }
         
     | 
| 
      
 322 
     | 
    
         
            +
            ```
         
     | 
| 
      
 323 
     | 
    
         
            +
             
     | 
| 
      
 324 
     | 
    
         
            +
            Tips
         
     | 
| 
      
 325 
     | 
    
         
            +
            =========
         
     | 
| 
      
 326 
     | 
    
         
            +
            ### Conditional Flow
         
     | 
| 
      
 327 
     | 
    
         
            +
            In a service, there is one and single flow, so if you need conditionals to branch off, you can do:
         
     | 
| 
      
 328 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 329 
     | 
    
         
            +
            self.chain { Service1.new }
         
     | 
| 
      
 330 
     | 
    
         
            +
             
     | 
| 
      
 331 
     | 
    
         
            +
            if foo?
         
     | 
| 
      
 332 
     | 
    
         
            +
              self.chain { Service2.new }
         
     | 
| 
      
 333 
     | 
    
         
            +
            else
         
     | 
| 
      
 334 
     | 
    
         
            +
              self.chain { Service3.new }
         
     | 
| 
      
 335 
     | 
    
         
            +
            end
         
     | 
| 
      
 336 
     | 
    
         
            +
            ```
         
     | 
| 
      
 337 
     | 
    
         
            +
             
     | 
| 
      
 338 
     | 
    
         
            +
             
     | 
| 
      
 339 
     | 
    
         
            +
            Examples
         
     | 
| 
      
 340 
     | 
    
         
            +
            =========
         
     | 
| 
      
 341 
     | 
    
         
            +
            Check the [wiki for other examples](https://github.com/apneadiving/waterfall/wiki).
         
     | 
| 
      
 342 
     | 
    
         
            +
             
     | 
| 
      
 343 
     | 
    
         
            +
            Thanks
         
     | 
| 
      
 344 
     | 
    
         
            +
            =========
         
     | 
| 
      
 345 
     | 
    
         
            +
            Huge thanks to [laxrph10](https://github.com/laxrph10) for the help during infinite naming brainstorming.
         
     | 
    
        data/lib/waterfall.rb
    ADDED
    
    | 
         @@ -0,0 +1,85 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'ostruct'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'waterfall/version'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'waterfall/predicates/base'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'waterfall/predicates/on_dam'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'waterfall/predicates/when_falsy'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'waterfall/predicates/when_truthy'
         
     | 
| 
      
 7 
     | 
    
         
            +
            require 'waterfall/predicates/chain'
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            module Waterfall
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              attr_reader :error_pool, :outflow, :flowing, :_wf_rolled_back
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              def when_falsy(&block)
         
     | 
| 
      
 14 
     | 
    
         
            +
                handler = ::Waterfall::WhenFalsy.new(self)
         
     | 
| 
      
 15 
     | 
    
         
            +
                _wf_run { handler.call(&block) }
         
     | 
| 
      
 16 
     | 
    
         
            +
                handler
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
              def when_truthy(&block)
         
     | 
| 
      
 20 
     | 
    
         
            +
                handler = ::Waterfall::WhenTruthy.new(self)
         
     | 
| 
      
 21 
     | 
    
         
            +
                _wf_run { handler.call(&block) }
         
     | 
| 
      
 22 
     | 
    
         
            +
                handler
         
     | 
| 
      
 23 
     | 
    
         
            +
              end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
              def chain(mapping_or_var_name = nil, &block)
         
     | 
| 
      
 26 
     | 
    
         
            +
                _wf_run do
         
     | 
| 
      
 27 
     | 
    
         
            +
                  ::Waterfall::Chain
         
     | 
| 
      
 28 
     | 
    
         
            +
                    .new(self, mapping_or_var_name)
         
     | 
| 
      
 29 
     | 
    
         
            +
                    .call(&block)
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
              end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
              def chain_wf(mapping_hash = nil, &block)
         
     | 
| 
      
 34 
     | 
    
         
            +
                chain(mapping_hash, &block)
         
     | 
| 
      
 35 
     | 
    
         
            +
              end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
              def on_dam(&block)
         
     | 
| 
      
 38 
     | 
    
         
            +
                ::Waterfall::OnDam
         
     | 
| 
      
 39 
     | 
    
         
            +
                  .new(self)
         
     | 
| 
      
 40 
     | 
    
         
            +
                  .call(&block)
         
     | 
| 
      
 41 
     | 
    
         
            +
                self
         
     | 
| 
      
 42 
     | 
    
         
            +
              end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
              def dam(obj)
         
     | 
| 
      
 45 
     | 
    
         
            +
                @error_pool = obj
         
     | 
| 
      
 46 
     | 
    
         
            +
                self
         
     | 
| 
      
 47 
     | 
    
         
            +
              end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
              def undam
         
     | 
| 
      
 50 
     | 
    
         
            +
                dam nil
         
     | 
| 
      
 51 
     | 
    
         
            +
                self
         
     | 
| 
      
 52 
     | 
    
         
            +
              end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
              def dammed?
         
     | 
| 
      
 55 
     | 
    
         
            +
                !error_pool.nil?
         
     | 
| 
      
 56 
     | 
    
         
            +
              end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
              def is_waterfall?
         
     | 
| 
      
 59 
     | 
    
         
            +
                true
         
     | 
| 
      
 60 
     | 
    
         
            +
              end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
              def flowing?
         
     | 
| 
      
 63 
     | 
    
         
            +
                !! @flowing
         
     | 
| 
      
 64 
     | 
    
         
            +
              end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
              def update_outflow(key, value)
         
     | 
| 
      
 67 
     | 
    
         
            +
                @outflow[key] = value
         
     | 
| 
      
 68 
     | 
    
         
            +
                self
         
     | 
| 
      
 69 
     | 
    
         
            +
              end
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
              def _wf_run
         
     | 
| 
      
 72 
     | 
    
         
            +
                @flowing = true
         
     | 
| 
      
 73 
     | 
    
         
            +
                @outflow ||= OpenStruct.new({})
         
     | 
| 
      
 74 
     | 
    
         
            +
                yield unless dammed?
         
     | 
| 
      
 75 
     | 
    
         
            +
                self
         
     | 
| 
      
 76 
     | 
    
         
            +
              end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
            end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
            class Wf
         
     | 
| 
      
 81 
     | 
    
         
            +
              include Waterfall
         
     | 
| 
      
 82 
     | 
    
         
            +
              def initialize
         
     | 
| 
      
 83 
     | 
    
         
            +
                @outflow = OpenStruct.new({})
         
     | 
| 
      
 84 
     | 
    
         
            +
              end
         
     | 
| 
      
 85 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,23 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Waterfall
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Base
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
                def waterfall?(obj)
         
     | 
| 
      
 5 
     | 
    
         
            +
                  obj.respond_to?(:is_waterfall?) && obj.is_waterfall?
         
     | 
| 
      
 6 
     | 
    
         
            +
                end
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                def chained_waterfall(child_waterfall)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  child_waterfall.call unless child_waterfall.flowing?
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  if child_waterfall.dammed?
         
     | 
| 
      
 12 
     | 
    
         
            +
                    @root.dam child_waterfall.error_pool
         
     | 
| 
      
 13 
     | 
    
         
            +
                  else
         
     | 
| 
      
 14 
     | 
    
         
            +
                    yield
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
                  self
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                def yield_args
         
     | 
| 
      
 20 
     | 
    
         
            +
                  [@root.outflow, @root]
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,26 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Waterfall
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Chain < Base
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
                def initialize(root, mapping_or_var_name)
         
     | 
| 
      
 5 
     | 
    
         
            +
                  @root, @mapping_or_var_name = root, mapping_or_var_name
         
     | 
| 
      
 6 
     | 
    
         
            +
                end
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                def call
         
     | 
| 
      
 9 
     | 
    
         
            +
                  output = yield(*yield_args)
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  if waterfall?(output)
         
     | 
| 
      
 12 
     | 
    
         
            +
                    map_waterfalls(output, @mapping_or_var_name || {})
         
     | 
| 
      
 13 
     | 
    
         
            +
                  else
         
     | 
| 
      
 14 
     | 
    
         
            +
                    @root.update_outflow(@mapping_or_var_name, output) if @mapping_or_var_name
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                def map_waterfalls(child_waterfall, mapping)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  chained_waterfall(child_waterfall) do
         
     | 
| 
      
 20 
     | 
    
         
            +
                    mapping.each do |k, v|
         
     | 
| 
      
 21 
     | 
    
         
            +
                      @root.update_outflow(k, child_waterfall.outflow[v])
         
     | 
| 
      
 22 
     | 
    
         
            +
                    end
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,19 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Waterfall
         
     | 
| 
      
 2 
     | 
    
         
            +
              class WhenFalsy < Base
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
                def initialize(root)
         
     | 
| 
      
 5 
     | 
    
         
            +
                  @root = root
         
     | 
| 
      
 6 
     | 
    
         
            +
                end
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                def call
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @output = yield(*yield_args)
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                def dam
         
     | 
| 
      
 13 
     | 
    
         
            +
                  if !@root.dammed? && !@output
         
     | 
| 
      
 14 
     | 
    
         
            +
                    @root.dam yield(*yield_args)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @root
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,19 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Waterfall
         
     | 
| 
      
 2 
     | 
    
         
            +
              class WhenTruthy < Base
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
                def initialize(root)
         
     | 
| 
      
 5 
     | 
    
         
            +
                  @root = root
         
     | 
| 
      
 6 
     | 
    
         
            +
                end
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                def call
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @output = yield(*yield_args)
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                def dam
         
     | 
| 
      
 13 
     | 
    
         
            +
                  if !@root.dammed? && @output
         
     | 
| 
      
 14 
     | 
    
         
            +
                    @root.dam yield(*yield_args)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @root
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,197 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe 'Wf' do
         
     | 
| 
      
 4 
     | 
    
         
            +
              let(:wf) { Wf.new }
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              describe "chain" do
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                it "yields wf outflow" do
         
     | 
| 
      
 9 
     | 
    
         
            +
                  wf
         
     | 
| 
      
 10 
     | 
    
         
            +
                    .chain  {|outflow| outflow[:bar] = 'bar' }
         
     | 
| 
      
 11 
     | 
    
         
            +
                    .chain  {|outflow| @bar = outflow[:bar] }
         
     | 
| 
      
 12 
     | 
    
         
            +
                  expect(wf.outflow[:bar]).to eq 'bar'
         
     | 
| 
      
 13 
     | 
    
         
            +
                  expect(@bar).to eq 'bar'
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                it "assigns outflow's key the value of the block" do
         
     | 
| 
      
 17 
     | 
    
         
            +
                  wf
         
     | 
| 
      
 18 
     | 
    
         
            +
                    .chain(:bar) { 'bar' }
         
     | 
| 
      
 19 
     | 
    
         
            +
                  expect(wf.outflow[:bar]).to eq 'bar'
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                context "wf internals" do
         
     | 
| 
      
 23 
     | 
    
         
            +
                  it "dam from within" do
         
     | 
| 
      
 24 
     | 
    
         
            +
                    wf
         
     | 
| 
      
 25 
     | 
    
         
            +
                      .chain  {|outflow, waterfall| waterfall.dam('errrrr') }
         
     | 
| 
      
 26 
     | 
    
         
            +
                      .on_dam {|error_pool| @errors = error_pool }
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                    expect(wf.dammed?).to be true
         
     | 
| 
      
 29 
     | 
    
         
            +
                    expect(@errors).to eq 'errrrr'
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  it "outflow from within" do
         
     | 
| 
      
 33 
     | 
    
         
            +
                    wf
         
     | 
| 
      
 34 
     | 
    
         
            +
                      .chain {|outflow, waterfall| waterfall.outflow[:foo] = 1 }
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                    expect(wf.outflow[:foo]).to eq 1
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                describe "chaining waterfalls" do
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                  shared_examples "a waterfall chain" do
         
     | 
| 
      
 43 
     | 
    
         
            +
                    describe 'chain_wf' do
         
     | 
| 
      
 44 
     | 
    
         
            +
                      it "takes expected vars only and rename them" do
         
     | 
| 
      
 45 
     | 
    
         
            +
                        wf
         
     | 
| 
      
 46 
     | 
    
         
            +
                          .chain_wf(baz: :foo) { waterfall }
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                        expect(wf.outflow.foo).to be nil
         
     | 
| 
      
 49 
     | 
    
         
            +
                        expect(wf.outflow.bar).to be nil
         
     | 
| 
      
 50 
     | 
    
         
            +
                        expect(wf.outflow[:baz]).to eq waterfall.outflow[:foo]
         
     | 
| 
      
 51 
     | 
    
         
            +
                      end
         
     | 
| 
      
 52 
     | 
    
         
            +
                    end
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  context "from an instance of a custom waterfall class" do
         
     | 
| 
      
 56 
     | 
    
         
            +
                    class FakeService
         
     | 
| 
      
 57 
     | 
    
         
            +
                      include Waterfall
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                      def call
         
     | 
| 
      
 60 
     | 
    
         
            +
                        self
         
     | 
| 
      
 61 
     | 
    
         
            +
                          .chain(:foo) { 1 }
         
     | 
| 
      
 62 
     | 
    
         
            +
                          .chain(:bar) { 2 }
         
     | 
| 
      
 63 
     | 
    
         
            +
                      end
         
     | 
| 
      
 64 
     | 
    
         
            +
                    end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                    let(:waterfall) { FakeService.new }
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                    it_behaves_like "a waterfall chain"
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                    context "only calls waterfall service if it was never called before" do
         
     | 
| 
      
 71 
     | 
    
         
            +
                      it "when passed as an instance responding to call" do
         
     | 
| 
      
 72 
     | 
    
         
            +
                        expect(waterfall).to receive(:call).once.and_call_original
         
     | 
| 
      
 73 
     | 
    
         
            +
                        wf
         
     | 
| 
      
 74 
     | 
    
         
            +
                          .chain { waterfall }
         
     | 
| 
      
 75 
     | 
    
         
            +
                      end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                      it "already called" do
         
     | 
| 
      
 78 
     | 
    
         
            +
                        expect(waterfall).to receive(:call).once.and_call_original
         
     | 
| 
      
 79 
     | 
    
         
            +
                        wf
         
     | 
| 
      
 80 
     | 
    
         
            +
                          .chain { waterfall.call }
         
     | 
| 
      
 81 
     | 
    
         
            +
                      end
         
     | 
| 
      
 82 
     | 
    
         
            +
                    end
         
     | 
| 
      
 83 
     | 
    
         
            +
                  end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                  context "from a mere wf" do
         
     | 
| 
      
 86 
     | 
    
         
            +
                    let(:waterfall) do
         
     | 
| 
      
 87 
     | 
    
         
            +
                      Wf.new
         
     | 
| 
      
 88 
     | 
    
         
            +
                        .chain(:foo) { 1 }
         
     | 
| 
      
 89 
     | 
    
         
            +
                        .chain(:bar) { 2 }
         
     | 
| 
      
 90 
     | 
    
         
            +
                    end
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                    it_behaves_like "a waterfall chain"
         
     | 
| 
      
 93 
     | 
    
         
            +
                  end
         
     | 
| 
      
 94 
     | 
    
         
            +
                end
         
     | 
| 
      
 95 
     | 
    
         
            +
              end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
              describe "when falsy" do
         
     | 
| 
      
 98 
     | 
    
         
            +
                let(:my_proc) { ->(val){ val } }
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
                def action(bool)
         
     | 
| 
      
 101 
     | 
    
         
            +
                  wf
         
     | 
| 
      
 102 
     | 
    
         
            +
                    .chain { wf.dam('dammed') if dam? }
         
     | 
| 
      
 103 
     | 
    
         
            +
                    .when_falsy { my_proc.call(bool) }
         
     | 
| 
      
 104 
     | 
    
         
            +
                      .dam  { 'err' }
         
     | 
| 
      
 105 
     | 
    
         
            +
                    .chain  { @foo = 1 }
         
     | 
| 
      
 106 
     | 
    
         
            +
                    .on_dam { |error_pool| @error = error_pool }
         
     | 
| 
      
 107 
     | 
    
         
            +
                end
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                context "main context not dammed" do
         
     | 
| 
      
 110 
     | 
    
         
            +
                  let(:dam?) { false }
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                  it "when actually falsy" do
         
     | 
| 
      
 113 
     | 
    
         
            +
                    action false
         
     | 
| 
      
 114 
     | 
    
         
            +
                    expect(@error).to eq 'err'
         
     | 
| 
      
 115 
     | 
    
         
            +
                    expect(@foo).to_not eq 1
         
     | 
| 
      
 116 
     | 
    
         
            +
                  end
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
                  it "when actually truthy" do
         
     | 
| 
      
 119 
     | 
    
         
            +
                    action true
         
     | 
| 
      
 120 
     | 
    
         
            +
                    expect(@error).to_not eq 'err'
         
     | 
| 
      
 121 
     | 
    
         
            +
                    expect(@foo).to eq 1
         
     | 
| 
      
 122 
     | 
    
         
            +
                  end
         
     | 
| 
      
 123 
     | 
    
         
            +
                end
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                context "main context dammed" do
         
     | 
| 
      
 126 
     | 
    
         
            +
                  let(:dam?) { true }
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
                  it "when actually falsy" do
         
     | 
| 
      
 129 
     | 
    
         
            +
                    expect(my_proc).to_not receive(:call)
         
     | 
| 
      
 130 
     | 
    
         
            +
                    action false
         
     | 
| 
      
 131 
     | 
    
         
            +
                  end
         
     | 
| 
      
 132 
     | 
    
         
            +
                end
         
     | 
| 
      
 133 
     | 
    
         
            +
              end
         
     | 
| 
      
 134 
     | 
    
         
            +
             
     | 
| 
      
 135 
     | 
    
         
            +
              describe "when truthy" do
         
     | 
| 
      
 136 
     | 
    
         
            +
                let(:my_proc) { ->(val){ val } }
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
                def action(bool)
         
     | 
| 
      
 139 
     | 
    
         
            +
                  wf
         
     | 
| 
      
 140 
     | 
    
         
            +
                    .chain { wf.dam('dammed') if dam? }
         
     | 
| 
      
 141 
     | 
    
         
            +
                    .when_truthy { my_proc.call(bool) }
         
     | 
| 
      
 142 
     | 
    
         
            +
                      .dam  { 'err' }
         
     | 
| 
      
 143 
     | 
    
         
            +
                    .chain  { @foo = 1 }
         
     | 
| 
      
 144 
     | 
    
         
            +
                    .on_dam { |error_pool| @error = error_pool }
         
     | 
| 
      
 145 
     | 
    
         
            +
                end
         
     | 
| 
      
 146 
     | 
    
         
            +
             
     | 
| 
      
 147 
     | 
    
         
            +
                context "main context not dammed" do
         
     | 
| 
      
 148 
     | 
    
         
            +
                  let(:dam?) { false }
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
                  it "when actually falsy" do
         
     | 
| 
      
 151 
     | 
    
         
            +
                    action false
         
     | 
| 
      
 152 
     | 
    
         
            +
                    expect(@error).to_not eq 'err'
         
     | 
| 
      
 153 
     | 
    
         
            +
                    expect(@foo).to eq 1
         
     | 
| 
      
 154 
     | 
    
         
            +
                  end
         
     | 
| 
      
 155 
     | 
    
         
            +
             
     | 
| 
      
 156 
     | 
    
         
            +
                  it "when actually truthy" do
         
     | 
| 
      
 157 
     | 
    
         
            +
                    action true
         
     | 
| 
      
 158 
     | 
    
         
            +
                    expect(@error).to eq 'err'
         
     | 
| 
      
 159 
     | 
    
         
            +
                    expect(@foo).to_not eq 1
         
     | 
| 
      
 160 
     | 
    
         
            +
                  end
         
     | 
| 
      
 161 
     | 
    
         
            +
                end
         
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
                context "main context dammed" do
         
     | 
| 
      
 164 
     | 
    
         
            +
                  let(:dam?) { true }
         
     | 
| 
      
 165 
     | 
    
         
            +
             
     | 
| 
      
 166 
     | 
    
         
            +
                  it "when actually truthy" do
         
     | 
| 
      
 167 
     | 
    
         
            +
                    expect(my_proc).to_not receive(:call)
         
     | 
| 
      
 168 
     | 
    
         
            +
                    action true
         
     | 
| 
      
 169 
     | 
    
         
            +
                  end
         
     | 
| 
      
 170 
     | 
    
         
            +
                end
         
     | 
| 
      
 171 
     | 
    
         
            +
              end
         
     | 
| 
      
 172 
     | 
    
         
            +
             
     | 
| 
      
 173 
     | 
    
         
            +
              describe "error propagation" do
         
     | 
| 
      
 174 
     | 
    
         
            +
                class FailingChain
         
     | 
| 
      
 175 
     | 
    
         
            +
                  include Waterfall
         
     | 
| 
      
 176 
     | 
    
         
            +
             
     | 
| 
      
 177 
     | 
    
         
            +
                  def call
         
     | 
| 
      
 178 
     | 
    
         
            +
                    self
         
     | 
| 
      
 179 
     | 
    
         
            +
                      .chain {|error_pool, waterfall| waterfall.dam(self.class.error) }
         
     | 
| 
      
 180 
     | 
    
         
            +
                  end
         
     | 
| 
      
 181 
     | 
    
         
            +
             
     | 
| 
      
 182 
     | 
    
         
            +
                  def self.error
         
     | 
| 
      
 183 
     | 
    
         
            +
                    'err'
         
     | 
| 
      
 184 
     | 
    
         
            +
                  end
         
     | 
| 
      
 185 
     | 
    
         
            +
                end
         
     | 
| 
      
 186 
     | 
    
         
            +
             
     | 
| 
      
 187 
     | 
    
         
            +
                it "error propagates" do
         
     | 
| 
      
 188 
     | 
    
         
            +
                  wf
         
     | 
| 
      
 189 
     | 
    
         
            +
                    .chain { FailingChain.new }
         
     | 
| 
      
 190 
     | 
    
         
            +
                    .chain    { @foo = 1 }
         
     | 
| 
      
 191 
     | 
    
         
            +
                    .on_dam   { |error_pool| @error = error_pool }
         
     | 
| 
      
 192 
     | 
    
         
            +
             
     | 
| 
      
 193 
     | 
    
         
            +
                  expect(@foo).to_not eq 1
         
     | 
| 
      
 194 
     | 
    
         
            +
                  expect(@error).to eq FailingChain.error
         
     | 
| 
      
 195 
     | 
    
         
            +
                end
         
     | 
| 
      
 196 
     | 
    
         
            +
              end
         
     | 
| 
      
 197 
     | 
    
         
            +
            end
         
     | 
    
        data/spec/spec_helper.rb
    ADDED
    
    | 
         @@ -0,0 +1,23 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # This file was generated by the `rspec --init` command. Conventionally, all
         
     | 
| 
      
 2 
     | 
    
         
            +
            # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
         
     | 
| 
      
 3 
     | 
    
         
            +
            # Require this file using `require "spec_helper"` to ensure that it is only
         
     | 
| 
      
 4 
     | 
    
         
            +
            # loaded once.
         
     | 
| 
      
 5 
     | 
    
         
            +
            #
         
     | 
| 
      
 6 
     | 
    
         
            +
            # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            require "codeclimate-test-reporter"
         
     | 
| 
      
 9 
     | 
    
         
            +
            CodeClimate::TestReporter.start
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            require File.expand_path('../../lib/waterfall', __FILE__)
         
     | 
| 
      
 12 
     | 
    
         
            +
            require 'pry'
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            RSpec.configure do |config|
         
     | 
| 
      
 15 
     | 
    
         
            +
              config.run_all_when_everything_filtered = true
         
     | 
| 
      
 16 
     | 
    
         
            +
              config.filter_run :focus
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              # Run specs in random order to surface order dependencies. If you find an
         
     | 
| 
      
 19 
     | 
    
         
            +
              # order dependency and want to debug it, you can fix the order by providing
         
     | 
| 
      
 20 
     | 
    
         
            +
              # the seed, which is printed after each run.
         
     | 
| 
      
 21 
     | 
    
         
            +
              #     --seed 1234
         
     | 
| 
      
 22 
     | 
    
         
            +
              config.order = 'random'
         
     | 
| 
      
 23 
     | 
    
         
            +
            end
         
     | 
    
        data/waterfall.gemspec
    ADDED
    
    | 
         @@ -0,0 +1,26 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # coding: utf-8
         
     | 
| 
      
 2 
     | 
    
         
            +
            lib = File.expand_path('../lib', __FILE__)
         
     | 
| 
      
 3 
     | 
    
         
            +
            $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'waterfall/version'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            Gem::Specification.new do |spec|
         
     | 
| 
      
 7 
     | 
    
         
            +
              spec.name          = "waterfall"
         
     | 
| 
      
 8 
     | 
    
         
            +
              spec.version       = Waterfall::VERSION
         
     | 
| 
      
 9 
     | 
    
         
            +
              spec.authors       = ["Benjamin Roth"]
         
     | 
| 
      
 10 
     | 
    
         
            +
              spec.email         = ["benjamin@rubyist.fr"]
         
     | 
| 
      
 11 
     | 
    
         
            +
              spec.description   = %q{A slice of functional programming to chain ruby services and blocks. Make them flow!}
         
     | 
| 
      
 12 
     | 
    
         
            +
              spec.summary       = %q{A slice of functional programming to chain ruby services and blocks. Make them flow!}
         
     | 
| 
      
 13 
     | 
    
         
            +
              spec.homepage      = "https://github.com/apneadiving/waterfall"
         
     | 
| 
      
 14 
     | 
    
         
            +
              spec.license       = "MIT"
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              spec.files         = `git ls-files`.split($/)
         
     | 
| 
      
 17 
     | 
    
         
            +
              spec.executables   = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
         
     | 
| 
      
 18 
     | 
    
         
            +
              spec.test_files    = spec.files.grep(%r{^(spec)/})
         
     | 
| 
      
 19 
     | 
    
         
            +
              spec.require_paths = ["lib"]
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
              spec.add_development_dependency "bundler", "~> 1.3"
         
     | 
| 
      
 22 
     | 
    
         
            +
              spec.add_development_dependency "pry", '>0.10'
         
     | 
| 
      
 23 
     | 
    
         
            +
              spec.add_development_dependency "pry-nav"
         
     | 
| 
      
 24 
     | 
    
         
            +
              spec.add_development_dependency "rake"
         
     | 
| 
      
 25 
     | 
    
         
            +
              spec.add_development_dependency "rspec", "3.2"
         
     | 
| 
      
 26 
     | 
    
         
            +
            end
         
     | 
    
        metadata
    ADDED
    
    | 
         @@ -0,0 +1,135 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            --- !ruby/object:Gem::Specification
         
     | 
| 
      
 2 
     | 
    
         
            +
            name: waterfall
         
     | 
| 
      
 3 
     | 
    
         
            +
            version: !ruby/object:Gem::Version
         
     | 
| 
      
 4 
     | 
    
         
            +
              version: 1.0.2
         
     | 
| 
      
 5 
     | 
    
         
            +
            platform: ruby
         
     | 
| 
      
 6 
     | 
    
         
            +
            authors:
         
     | 
| 
      
 7 
     | 
    
         
            +
            - Benjamin Roth
         
     | 
| 
      
 8 
     | 
    
         
            +
            autorequire: 
         
     | 
| 
      
 9 
     | 
    
         
            +
            bindir: bin
         
     | 
| 
      
 10 
     | 
    
         
            +
            cert_chain: []
         
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2015-12-27 00:00:00.000000000 Z
         
     | 
| 
      
 12 
     | 
    
         
            +
            dependencies:
         
     | 
| 
      
 13 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 14 
     | 
    
         
            +
              name: bundler
         
     | 
| 
      
 15 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 16 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 17 
     | 
    
         
            +
                - - ~>
         
     | 
| 
      
 18 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 19 
     | 
    
         
            +
                    version: '1.3'
         
     | 
| 
      
 20 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 21 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 22 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 23 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 24 
     | 
    
         
            +
                - - ~>
         
     | 
| 
      
 25 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 26 
     | 
    
         
            +
                    version: '1.3'
         
     | 
| 
      
 27 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 28 
     | 
    
         
            +
              name: pry
         
     | 
| 
      
 29 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 30 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 31 
     | 
    
         
            +
                - - '>'
         
     | 
| 
      
 32 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 33 
     | 
    
         
            +
                    version: '0.10'
         
     | 
| 
      
 34 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 35 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 36 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 37 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 38 
     | 
    
         
            +
                - - '>'
         
     | 
| 
      
 39 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 40 
     | 
    
         
            +
                    version: '0.10'
         
     | 
| 
      
 41 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 42 
     | 
    
         
            +
              name: pry-nav
         
     | 
| 
      
 43 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 44 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 45 
     | 
    
         
            +
                - - '>='
         
     | 
| 
      
 46 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 47 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 48 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 49 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 50 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 51 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 52 
     | 
    
         
            +
                - - '>='
         
     | 
| 
      
 53 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 54 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 55 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 56 
     | 
    
         
            +
              name: rake
         
     | 
| 
      
 57 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 58 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 59 
     | 
    
         
            +
                - - '>='
         
     | 
| 
      
 60 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 61 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 62 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 63 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 64 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 65 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 66 
     | 
    
         
            +
                - - '>='
         
     | 
| 
      
 67 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 68 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 69 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 70 
     | 
    
         
            +
              name: rspec
         
     | 
| 
      
 71 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 72 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 73 
     | 
    
         
            +
                - - '='
         
     | 
| 
      
 74 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 75 
     | 
    
         
            +
                    version: '3.2'
         
     | 
| 
      
 76 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 77 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 78 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 79 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 80 
     | 
    
         
            +
                - - '='
         
     | 
| 
      
 81 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 82 
     | 
    
         
            +
                    version: '3.2'
         
     | 
| 
      
 83 
     | 
    
         
            +
            description: A slice of functional programming to chain ruby services and blocks.
         
     | 
| 
      
 84 
     | 
    
         
            +
              Make them flow!
         
     | 
| 
      
 85 
     | 
    
         
            +
            email:
         
     | 
| 
      
 86 
     | 
    
         
            +
            - benjamin@rubyist.fr
         
     | 
| 
      
 87 
     | 
    
         
            +
            executables: []
         
     | 
| 
      
 88 
     | 
    
         
            +
            extensions: []
         
     | 
| 
      
 89 
     | 
    
         
            +
            extra_rdoc_files: []
         
     | 
| 
      
 90 
     | 
    
         
            +
            files:
         
     | 
| 
      
 91 
     | 
    
         
            +
            - .gitignore
         
     | 
| 
      
 92 
     | 
    
         
            +
            - .rspec
         
     | 
| 
      
 93 
     | 
    
         
            +
            - .travis.yml
         
     | 
| 
      
 94 
     | 
    
         
            +
            - Gemfile
         
     | 
| 
      
 95 
     | 
    
         
            +
            - Gemfile.lock
         
     | 
| 
      
 96 
     | 
    
         
            +
            - LICENSE.txt
         
     | 
| 
      
 97 
     | 
    
         
            +
            - README.md
         
     | 
| 
      
 98 
     | 
    
         
            +
            - lib/waterfall.rb
         
     | 
| 
      
 99 
     | 
    
         
            +
            - lib/waterfall/predicates/base.rb
         
     | 
| 
      
 100 
     | 
    
         
            +
            - lib/waterfall/predicates/chain.rb
         
     | 
| 
      
 101 
     | 
    
         
            +
            - lib/waterfall/predicates/on_dam.rb
         
     | 
| 
      
 102 
     | 
    
         
            +
            - lib/waterfall/predicates/when_falsy.rb
         
     | 
| 
      
 103 
     | 
    
         
            +
            - lib/waterfall/predicates/when_truthy.rb
         
     | 
| 
      
 104 
     | 
    
         
            +
            - lib/waterfall/version.rb
         
     | 
| 
      
 105 
     | 
    
         
            +
            - spec/integration_spec.rb
         
     | 
| 
      
 106 
     | 
    
         
            +
            - spec/spec_helper.rb
         
     | 
| 
      
 107 
     | 
    
         
            +
            - waterfall.gemspec
         
     | 
| 
      
 108 
     | 
    
         
            +
            homepage: https://github.com/apneadiving/waterfall
         
     | 
| 
      
 109 
     | 
    
         
            +
            licenses:
         
     | 
| 
      
 110 
     | 
    
         
            +
            - MIT
         
     | 
| 
      
 111 
     | 
    
         
            +
            metadata: {}
         
     | 
| 
      
 112 
     | 
    
         
            +
            post_install_message: 
         
     | 
| 
      
 113 
     | 
    
         
            +
            rdoc_options: []
         
     | 
| 
      
 114 
     | 
    
         
            +
            require_paths:
         
     | 
| 
      
 115 
     | 
    
         
            +
            - lib
         
     | 
| 
      
 116 
     | 
    
         
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         
     | 
| 
      
 117 
     | 
    
         
            +
              requirements:
         
     | 
| 
      
 118 
     | 
    
         
            +
              - - '>='
         
     | 
| 
      
 119 
     | 
    
         
            +
                - !ruby/object:Gem::Version
         
     | 
| 
      
 120 
     | 
    
         
            +
                  version: '0'
         
     | 
| 
      
 121 
     | 
    
         
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         
     | 
| 
      
 122 
     | 
    
         
            +
              requirements:
         
     | 
| 
      
 123 
     | 
    
         
            +
              - - '>='
         
     | 
| 
      
 124 
     | 
    
         
            +
                - !ruby/object:Gem::Version
         
     | 
| 
      
 125 
     | 
    
         
            +
                  version: '0'
         
     | 
| 
      
 126 
     | 
    
         
            +
            requirements: []
         
     | 
| 
      
 127 
     | 
    
         
            +
            rubyforge_project: 
         
     | 
| 
      
 128 
     | 
    
         
            +
            rubygems_version: 2.0.3
         
     | 
| 
      
 129 
     | 
    
         
            +
            signing_key: 
         
     | 
| 
      
 130 
     | 
    
         
            +
            specification_version: 4
         
     | 
| 
      
 131 
     | 
    
         
            +
            summary: A slice of functional programming to chain ruby services and blocks. Make
         
     | 
| 
      
 132 
     | 
    
         
            +
              them flow!
         
     | 
| 
      
 133 
     | 
    
         
            +
            test_files:
         
     | 
| 
      
 134 
     | 
    
         
            +
            - spec/integration_spec.rb
         
     | 
| 
      
 135 
     | 
    
         
            +
            - spec/spec_helper.rb
         
     |