pipeable 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/README.adoc +227 -70
- data/lib/pipeable/steps/abstract.rb +1 -3
- data/lib/pipeable/steps/as.rb +2 -2
- data/lib/pipeable/steps/bind.rb +1 -1
- data/lib/pipeable/steps/check.rb +13 -11
- data/lib/pipeable/steps/container.rb +1 -1
- data/lib/pipeable/steps/fmap.rb +1 -1
- data/lib/pipeable/steps/insert.rb +3 -3
- data/lib/pipeable/steps/map.rb +1 -1
- data/lib/pipeable/steps/merge.rb +7 -7
- data/lib/pipeable/steps/or.rb +1 -1
- data/lib/pipeable/steps/tee.rb +1 -1
- data/lib/pipeable/steps/to.rb +8 -6
- data/lib/pipeable/steps/try.rb +5 -5
- data/lib/pipeable/steps/use.rb +5 -5
- data/lib/pipeable/steps/validate.rb +10 -14
- data/pipeable.gemspec +1 -1
- data.tar.gz.sig +0 -0
- metadata +3 -3
- metadata.gz.sig +0 -0
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 2467c8bb8dda8efd0a2e4fc5a78765f8abaeab65c288c380d9c7358244ba5aaf
         | 
| 4 | 
            +
              data.tar.gz: 96c96e5e12d25f81dd9c7e83b59850691036513b8456acab4868b93a71083f22
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 94f1002860d093423864900306a266a8c5d452f186f40e6053c326fc9a689cf0b7edded0a60f92d00dd1de14f906315f6b88a7987fb0b67511f9a92fdd73f160
         | 
| 7 | 
            +
              data.tar.gz: 767a44496263b160871c38a7bef274eacc089dbf641fdb60a7879f07e584603c0fc125cf9b285c156a75daff4e6cc856c5b027307f87a8f1f34c0876dcba33ce
         | 
    
        checksums.yaml.gz.sig
    CHANGED
    
    | Binary file | 
    
        data/README.adoc
    CHANGED
    
    | @@ -10,11 +10,12 @@ | |
| 10 10 | 
             
            :dry_validation_link: link:https://dry-rb.org/gems/dry-validation[Dry Validation]
         | 
| 11 11 | 
             
            :function_composition_link: link:https://alchemists.io/articles/ruby_function_composition[Function Composition]
         | 
| 12 12 | 
             
            :infusible_link: link:https://alchemists.io/projects/infusible[Infusible]
         | 
| 13 | 
            +
            :method_parameters_and_arguments_link: link:https://alchemists.io/articles/ruby_method_parameters_and_arguments[Method Parameters And Arguments]
         | 
| 13 14 | 
             
            :railway_pattern_link: link:https://fsharpforfunandprofit.com/posts/recipe-part2[Railway Pattern]
         | 
| 14 15 |  | 
| 15 16 | 
             
            = Pipeable
         | 
| 16 17 |  | 
| 17 | 
            -
            A DSL for workflows built atop native {function_composition_link} which leverages the {railway_pattern_link}. This allows you to write a sequence of _steps_ that cleanly read from  | 
| 18 | 
            +
            A DSL for workflows built atop native {function_composition_link} which leverages the {railway_pattern_link}. This allows you to write a sequence of _steps_ that cleanly read from top-to-bottom or left-to-right resulting in a single success or a failure. This allows you to avoid relying on exceptions for expensive control flows and/or complex conditional logic in general.
         | 
| 18 19 |  | 
| 19 20 | 
             
            toc::[]
         | 
| 20 21 |  | 
| @@ -29,7 +30,7 @@ toc::[] | |
| 29 30 | 
             
            == Requirements
         | 
| 30 31 |  | 
| 31 32 | 
             
            . link:https://www.ruby-lang.org[Ruby].
         | 
| 32 | 
            -
            . A strong understanding of {function_composition_link}.
         | 
| 33 | 
            +
            . A strong understanding of {function_composition_link} and {method_parameters_and_arguments_link}.
         | 
| 33 34 |  | 
| 34 35 | 
             
            == Setup
         | 
| 35 36 |  | 
| @@ -65,7 +66,7 @@ require "pipeable" | |
| 65 66 |  | 
| 66 67 | 
             
            == Usage
         | 
| 67 68 |  | 
| 68 | 
            -
            You can turn any object into a  | 
| 69 | 
            +
            You can turn any object into a _pipe_ by requiring and including this gem as follows:
         | 
| 69 70 |  | 
| 70 71 | 
             
            [source,ruby]
         | 
| 71 72 | 
             
            ----
         | 
| @@ -100,7 +101,7 @@ class Demo | |
| 100 101 | 
             
            end
         | 
| 101 102 | 
             
            ----
         | 
| 102 103 |  | 
| 103 | 
            -
            The above allows `Demo#call` to be a sequence steps which may pass or fail due to all  | 
| 104 | 
            +
            The above allows `Demo#call` to be a sequence of steps which may pass or fail due to all steps using {dry_monads_link} for input and output. This is the essence of the {railway_pattern_link}.
         | 
| 104 105 |  | 
| 105 106 | 
             
            To execute the above example, you'd only need to pass CSV content to it:
         | 
| 106 107 |  | 
| @@ -151,19 +152,44 @@ Then the above would look like this using native Ruby: | |
| 151 152 | 
             
            ).call Success(csv)
         | 
| 152 153 | 
             
            ----
         | 
| 153 154 |  | 
| 154 | 
            -
            The problem with native function composition is that it reads backwards by passing  | 
| 155 | 
            +
            The problem with native function composition is that it reads backwards by passing input at the end of all sequential steps. With the `#pipe` method, you have the benefit of allowing your eyes to read from top to bottom while not having to type multiple _forward composition_ operators.
         | 
| 155 156 |  | 
| 156 157 | 
             
            === Steps
         | 
| 157 158 |  | 
| 158 | 
            -
            There are several ways to compose steps for your pipe. As long as all steps succeed, you'll get a successful response. Otherwise, the first step to fail will pass the failure down by skipping all subsequent steps (unless you dynamically  | 
| 159 | 
            +
            There are several ways to compose steps for your pipe. As long as all steps succeed, you'll get a successful response. Otherwise, the first step to fail will pass the failure down by skipping all subsequent steps (unless you dynamically turn the failure into a success).
         | 
| 160 | 
            +
             | 
| 161 | 
            +
            Each step expects a {dry_monads_link} `Result` object as input and answers either the same or new `Result` object for consumption by the next step in the pipe. Additionally, each step will either unwrap the `Result` or pass the `Result` through depending on the step's implementation. These details are noted in each step's documentation below complete with _i/o_ (input/output) and _example_ sections.
         | 
| 159 162 |  | 
| 160 163 | 
             
            ==== Basic
         | 
| 161 164 |  | 
| 162 | 
            -
            The following are the basic (default) steps for building for  | 
| 165 | 
            +
            The following are the basic (default) steps for building custom pipes for which you can mix and match within your own implementation.
         | 
| 166 | 
            +
             | 
| 167 | 
            +
            ===== Alt
         | 
| 168 | 
            +
             | 
| 169 | 
            +
            Allows you to operate on a failure and produce either a success or another failure. This is a convenience wrapper to native {dry_monads_link} `#or` functionality.
         | 
| 170 | 
            +
             | 
| 171 | 
            +
            *I/O*
         | 
| 172 | 
            +
             | 
| 173 | 
            +
            Processes a failure only while expecting you to answer a success or failure.
         | 
| 174 | 
            +
             | 
| 175 | 
            +
            *Example*
         | 
| 176 | 
            +
             | 
| 177 | 
            +
            [source,ruby]
         | 
| 178 | 
            +
            ----
         | 
| 179 | 
            +
            pipe %i[a b c], alt { |object| Success object.join("-") }          # Success [:a, :b, :c]
         | 
| 180 | 
            +
            pipe Failure("Danger!"), alt { Success "Resolved" }                # Success "Resolved"
         | 
| 181 | 
            +
            pipe Failure("Danger!"), alt { |object| Failure "Big #{object}" }  # Failure "Big Danger!"
         | 
| 182 | 
            +
            ----
         | 
| 163 183 |  | 
| 164 184 | 
             
            ===== As
         | 
| 165 185 |  | 
| 166 | 
            -
            Allows you to message  | 
| 186 | 
            +
            Allows you to message an object as a different result. The first argument is the method but additional positional and/or keyword arguments can be passed along if the method accepts them.
         | 
| 187 | 
            +
             | 
| 188 | 
            +
            *I/O*
         | 
| 189 | 
            +
             | 
| 190 | 
            +
            Processes and answers a success only.
         | 
| 191 | 
            +
             | 
| 192 | 
            +
            *Example*
         | 
| 167 193 |  | 
| 168 194 | 
             
            [source,ruby]
         | 
| 169 195 | 
             
            ----
         | 
| @@ -174,18 +200,30 @@ pipe Failure("Danger!"), as(:inspect)  # Failure "Danger!" | |
| 174 200 |  | 
| 175 201 | 
             
            ===== Bind
         | 
| 176 202 |  | 
| 177 | 
            -
            Allows you to perform operations  | 
| 203 | 
            +
            Allows you to perform operations upon success only. You are then responsible for answering a success or failure accordingly. This is a convenience wrapper to native {dry_monads_link} `#bind` functionality.
         | 
| 204 | 
            +
             | 
| 205 | 
            +
            *I/O*
         | 
| 206 | 
            +
             | 
| 207 | 
            +
            Processes a success only while expecting you to answer a success or failure in return.
         | 
| 208 | 
            +
             | 
| 209 | 
            +
            *Example*
         | 
| 178 210 |  | 
| 179 211 | 
             
            [source,ruby]
         | 
| 180 212 | 
             
            ----
         | 
| 181 | 
            -
            pipe %i[a b c], bind { | | 
| 182 | 
            -
            pipe %i[a b c], bind { | | 
| 183 | 
            -
            pipe Failure("Danger!"), bind { | | 
| 213 | 
            +
            pipe %i[a b c], bind { |object| Success object.join("-") }           # Success "a-b-c"
         | 
| 214 | 
            +
            pipe %i[a b c], bind { |object| Failure object }                     # Failure [:a, :b, :c]
         | 
| 215 | 
            +
            pipe Failure("Danger!"), bind { |object| Success object.join("-") }  # Failure "Danger!"
         | 
| 184 216 | 
             
            ----
         | 
| 185 217 |  | 
| 186 218 | 
             
            ===== Check
         | 
| 187 219 |  | 
| 188 | 
            -
            Allows you to check if the  | 
| 220 | 
            +
            Allows you to check if an object matches the proof (with message). The first argument is your proof while the second argument is the message to send to your proof. A check only passes if the messaged object evaluates to `true` or `Success`. When successful, the object is passed through as a `Success`. When false, the object is passed through as a `Failure`.
         | 
| 221 | 
            +
             | 
| 222 | 
            +
            *I/O*
         | 
| 223 | 
            +
             | 
| 224 | 
            +
            Processes a success only while answering a success or failure depending on whether unwrapped object checks against the proof.
         | 
| 225 | 
            +
             | 
| 226 | 
            +
            *Example*
         | 
| 189 227 |  | 
| 190 228 | 
             
            [source,ruby]
         | 
| 191 229 | 
             
            ----
         | 
| @@ -196,17 +234,29 @@ pipe Failure("Danger!"), check(%i[a b], :include?)  # Failure "Danger!" | |
| 196 234 |  | 
| 197 235 | 
             
            ===== Fmap
         | 
| 198 236 |  | 
| 199 | 
            -
            Allows you to unwrap a  | 
| 237 | 
            +
            Allows you to unwrap a success, make a modification, and rewrap the modification as a new success. This is a convenience wrapper to native {dry_monads_link} `#fmap` functionality.
         | 
| 238 | 
            +
             | 
| 239 | 
            +
            *I/O*
         | 
| 240 | 
            +
             | 
| 241 | 
            +
            Processes and answers a success only.
         | 
| 242 | 
            +
             | 
| 243 | 
            +
            *Example*
         | 
| 200 244 |  | 
| 201 245 | 
             
            [source,ruby]
         | 
| 202 246 | 
             
            ----
         | 
| 203 | 
            -
            pipe %i[a b c], fmap { | | 
| 204 | 
            -
            pipe Failure("Danger!"), fmap { | | 
| 247 | 
            +
            pipe %i[a b c], fmap { |object| object.join "-" }           # Success "a-b-c"
         | 
| 248 | 
            +
            pipe Failure("Danger!"), fmap { |object| object.join "-" }  # Failure "Danger!"
         | 
| 205 249 | 
             
            ----
         | 
| 206 250 |  | 
| 207 251 | 
             
            ===== Insert
         | 
| 208 252 |  | 
| 209 | 
            -
            Allows you to insert an element after  | 
| 253 | 
            +
            Allows you to insert an element after an object (default behavior). This step wraps native link:https://rubyapi.org/o/array#method-i-insert[Array#insert] functionality. If the object is not an array, it will be cast as one. You can use the `:at` key to specify where you want insertion to happen. This step is most useful when needing to assemble arguments for passing to a subsequent step.
         | 
| 254 | 
            +
             | 
| 255 | 
            +
            *I/O*
         | 
| 256 | 
            +
             | 
| 257 | 
            +
            Processes and answers a success only.
         | 
| 258 | 
            +
             | 
| 259 | 
            +
            *Example*
         | 
| 210 260 |  | 
| 211 261 | 
             
            [source,ruby]
         | 
| 212 262 | 
             
            ----
         | 
| @@ -218,7 +268,13 @@ pipe Failure("Danger!"), insert(:b)  # Failure "Danger!" | |
| 218 268 |  | 
| 219 269 | 
             
            ===== Map
         | 
| 220 270 |  | 
| 221 | 
            -
            Allows you to map over an enumerable  | 
| 271 | 
            +
            Allows you to map over an object (enumerable) by wrapping native link:https://rubyapi.org/o/enumerable#method-i-map[Enumerable#map] functionality.
         | 
| 272 | 
            +
             | 
| 273 | 
            +
            *I/O*
         | 
| 274 | 
            +
             | 
| 275 | 
            +
            Processes and answers a success only.
         | 
| 276 | 
            +
             | 
| 277 | 
            +
            *Example*
         | 
| 222 278 |  | 
| 223 279 | 
             
            [source,ruby]
         | 
| 224 280 | 
             
            ----
         | 
| @@ -228,7 +284,13 @@ pipe Failure("Danger!"), map(&:inspect)  # Failure "Danger!" | |
| 228 284 |  | 
| 229 285 | 
             
            ===== Merge
         | 
| 230 286 |  | 
| 231 | 
            -
            Allows you to merge  | 
| 287 | 
            +
            Allows you to merge an object with additional attributes as a single hash. If the input is not a hash, then the object will be merged with `step` as the key. The default `step` key can be renamed to a different key by using the `:as` key. Like the _Insert_ step, this is most useful when assembling arguments and/or data for consumption by subsequent steps
         | 
| 288 | 
            +
             | 
| 289 | 
            +
            *I/O*
         | 
| 290 | 
            +
             | 
| 291 | 
            +
            Processes and answers a success only.
         | 
| 292 | 
            +
             | 
| 293 | 
            +
            *Example*
         | 
| 232 294 |  | 
| 233 295 | 
             
            [source,ruby]
         | 
| 234 296 | 
             
            ----
         | 
| @@ -238,24 +300,15 @@ pipe "test", merge(as: :a, b: 2)      # Success {a: "test", b: 2} | |
| 238 300 | 
             
            pipe Failure("Danger!"), merge(b: 2)  # Failure "Danger!"
         | 
| 239 301 | 
             
            ----
         | 
| 240 302 |  | 
| 241 | 
            -
            =====  | 
| 242 | 
            -
             | 
| 243 | 
            -
            Allows you to operate on a failure and produce either a success or another failure. This is a convenience wrapper to native {dry_monads_link} `#or` functionality.
         | 
| 244 | 
            -
             | 
| 245 | 
            -
            ℹ️ Syntactically, `or` can't be used for this step since `or` is a native Ruby keyword so `orr` is used instead.
         | 
| 303 | 
            +
            ===== Tee
         | 
| 246 304 |  | 
| 247 | 
            -
             | 
| 305 | 
            +
            Allows you to run an operation and ignore the response while input is passed through as output. This behavior is similar in nature to the link:https://www.gnu.org/savannah-checkouts/gnu/gawk/manual/html_node/Tee-Program.html[tee] program in Bash.
         | 
| 248 306 |  | 
| 249 | 
            -
             | 
| 250 | 
            -
            ----
         | 
| 251 | 
            -
            pipe %i[a b c], orr { |input| Success input.join("-") }          # Success [:a, :b, :c]
         | 
| 252 | 
            -
            pipe Failure("Danger!"), orr { Success "Resolved" }              # Success "Resolved"
         | 
| 253 | 
            -
            pipe Failure("Danger!"), orr { |input| Failure "Big #{input}" }  # Failure "Big Danger!"
         | 
| 254 | 
            -
            ----
         | 
| 307 | 
            +
            *I/O*
         | 
| 255 308 |  | 
| 256 | 
            -
             | 
| 309 | 
            +
            Passes the result through while allowing you to execute arbitrary behavior.
         | 
| 257 310 |  | 
| 258 | 
            -
             | 
| 311 | 
            +
            *Example*
         | 
| 259 312 |  | 
| 260 313 | 
             
            [source,ruby]
         | 
| 261 314 | 
             
            ----
         | 
| @@ -272,14 +325,20 @@ pipe Failure("Danger!"), tee(Kernel, :puts, "Example.") | |
| 272 325 |  | 
| 273 326 | 
             
            ===== To
         | 
| 274 327 |  | 
| 275 | 
            -
            Allows you to delegate to an object  | 
| 328 | 
            +
            Allows you to delegate to an object which doesn't have a callable interface and may or may not answer a result. If the response is not a monad, it'll be automatically wrapped as a `Success`.
         | 
| 329 | 
            +
             | 
| 330 | 
            +
            *I/O*
         | 
| 331 | 
            +
             | 
| 332 | 
            +
            Processes a success only while sending the unwrapped object to the given object's corresponding method. The object is expected to answer either a plain Ruby object which will be automatically wrapped as a success or a {dry_monads_link} `Result`.
         | 
| 333 | 
            +
             | 
| 334 | 
            +
            *Example*
         | 
| 276 335 |  | 
| 277 336 | 
             
            [source,ruby]
         | 
| 278 337 | 
             
            ----
         | 
| 279 | 
            -
            Model = Struct.new :label | 
| 338 | 
            +
            Model = Struct.new :label do
         | 
| 280 339 | 
             
              include Dry::Monads[:result]
         | 
| 281 340 |  | 
| 282 | 
            -
              def self.for( | 
| 341 | 
            +
              def self.for(**) = Success new(**)
         | 
| 283 342 | 
             
            end
         | 
| 284 343 |  | 
| 285 344 | 
             
            pipe({label: "Test"}, to(Model, :for))    # Success #<struct Model label="Test">
         | 
| @@ -288,22 +347,42 @@ pipe Failure("Danger!"), to(Model, :for)  # Failure "Danger!" | |
| 288 347 |  | 
| 289 348 | 
             
            ===== Try
         | 
| 290 349 |  | 
| 291 | 
            -
            Allows you to try an operation which may fail while catching  | 
| 350 | 
            +
            Allows you to try an operation which may fail while catching any exceptions as a failure for further processing. You can catch a single exception by providing the exception as a single value or multiple exceptions as an array of values.
         | 
| 351 | 
            +
             | 
| 352 | 
            +
            *I/O*
         | 
| 353 | 
            +
             | 
| 354 | 
            +
            Processes and answers a success only if there are no exceptions. Otherwise, captures any error as a failure.
         | 
| 355 | 
            +
             | 
| 356 | 
            +
            *Example*
         | 
| 292 357 |  | 
| 293 358 | 
             
            [source,ruby]
         | 
| 294 359 | 
             
            ----
         | 
| 295 | 
            -
            pipe "test", try(:to_json, catch: JSON::ParserError) | 
| 296 | 
            -
             | 
| 297 | 
            -
             | 
| 360 | 
            +
            pipe "test", try(:to_json, catch: JSON::ParserError)
         | 
| 361 | 
            +
            # Success "\"test\""
         | 
| 362 | 
            +
             | 
| 363 | 
            +
            pipe "test", try(:to_json, catch: [JSON::ParserError, StandardError])
         | 
| 364 | 
            +
            # Success "\"test\""
         | 
| 365 | 
            +
             | 
| 366 | 
            +
            pipe "test", try(:invalid, catch: NoMethodError)
         | 
| 367 | 
            +
            # Failure(#<NoMethodError: undefined method `invalid' for an instance of String>)
         | 
| 368 | 
            +
             | 
| 369 | 
            +
            pipe Failure("Danger!"), try(:to_json, catch: JSON::ParserError)
         | 
| 370 | 
            +
            # Failure "Danger!"
         | 
| 298 371 | 
             
            ----
         | 
| 299 372 |  | 
| 300 373 | 
             
            ===== Use
         | 
| 301 374 |  | 
| 302 | 
            -
            Allows you to use another  | 
| 375 | 
            +
            Allows you to use another pipe to build a superpipe, use an object that adheres to the {command_pattern_link}, or any function which answers a {dry_monads_link} `Result` object. In other words, you can use _use_ any object which responds to `#call` that answers a {dry_monads_link} `Result` object. This is great for chaining multiple pipes together (i.e. superpipes).
         | 
| 376 | 
            +
             | 
| 377 | 
            +
            *I/O*
         | 
| 378 | 
            +
             | 
| 379 | 
            +
            Processes a success only while sending the unwrapped object to the command (or pipe) for further processing. A {dry_monads_link} `Result` is expected to be answered by the command.
         | 
| 380 | 
            +
             | 
| 381 | 
            +
            *Example*
         | 
| 303 382 |  | 
| 304 383 | 
             
            [source,ruby]
         | 
| 305 384 | 
             
            ----
         | 
| 306 | 
            -
            function = ->  | 
| 385 | 
            +
            function = -> number { Success number * 3 }
         | 
| 307 386 |  | 
| 308 387 | 
             
            pipe 3, use(function)                   # Success 9
         | 
| 309 388 | 
             
            pipe Failure("Danger!"), use(function)  # Failure "Danger!"
         | 
| @@ -311,17 +390,30 @@ pipe Failure("Danger!"), use(function)  # Failure "Danger!" | |
| 311 390 |  | 
| 312 391 | 
             
            ===== Validate
         | 
| 313 392 |  | 
| 314 | 
            -
            Allows you to use an  | 
| 393 | 
            +
            Allows you to use an contract for validating an object. This is especially useful when using {dry_schema_link}, {dry_validation_link}, or any contract that responds to `#call` and answers a `Result`.
         | 
| 394 | 
            +
             | 
| 395 | 
            +
            💡 Ensure you enable the {dry_monads_link} extension for {dry_schema_link} and/or {dry_validation_link} when using this step since this step expects the contract to respond to the `#to_monad` message.
         | 
| 396 | 
            +
             | 
| 397 | 
            +
            By default, the `:as` key's value is `nil``. Use `:to_h`, for example, as the value for automatic casting to a `Hash`. You can also pass in any value to the `:as` key which is a valid method that the contract's result will respond to.
         | 
| 315 398 |  | 
| 316 | 
            -
             | 
| 399 | 
            +
            *I/O*
         | 
| 400 | 
            +
             | 
| 401 | 
            +
            Processes a success only. A success will be rewrapped as a success if the `:as` keyword is supplied. Otherwise, any failure is immediately passed through.
         | 
| 402 | 
            +
             | 
| 403 | 
            +
            *Example*
         | 
| 317 404 |  | 
| 318 405 | 
             
            [source,ruby]
         | 
| 319 406 | 
             
            ----
         | 
| 320 407 | 
             
            schema = Dry::Schema.Params { required(:label).filled :string }
         | 
| 321 408 |  | 
| 322 | 
            -
            pipe({label: "Test"}, validate(schema)) | 
| 323 | 
            -
             | 
| 324 | 
            -
             | 
| 409 | 
            +
            pipe({label: "Test"}, validate(schema))
         | 
| 410 | 
            +
            # Success label: "Test"
         | 
| 411 | 
            +
             | 
| 412 | 
            +
            pipe({label: "Test"}, validate(schema, as: nil))
         | 
| 413 | 
            +
            # Success #<Dry::Schema::Result{:label=>"Test"} errors={} path=[]>
         | 
| 414 | 
            +
             | 
| 415 | 
            +
            pipe Failure("Danger!"), validate(schema)
         | 
| 416 | 
            +
            # Failure "Danger!"
         | 
| 325 417 | 
             
            ----
         | 
| 326 418 |  | 
| 327 419 | 
             
            ==== Advanced
         | 
| @@ -334,8 +426,8 @@ You can always use a `Proc` as a custom step. Example: | |
| 334 426 |  | 
| 335 427 | 
             
            [source,ruby]
         | 
| 336 428 | 
             
            ----
         | 
| 337 | 
            -
            include Pipeable
         | 
| 338 429 | 
             
            include Dry::Monads[:result]
         | 
| 430 | 
            +
            include Pipeable
         | 
| 339 431 |  | 
| 340 432 | 
             
            pipe :a,
         | 
| 341 433 | 
             
                 insert(:b),
         | 
| @@ -355,7 +447,7 @@ include Pipeable | |
| 355 447 |  | 
| 356 448 | 
             
            pipe :a,
         | 
| 357 449 | 
             
                 insert(:b),
         | 
| 358 | 
            -
                 -> result { result.fmap { | | 
| 450 | 
            +
                 -> result { result.fmap { |items| items.join "_" } },
         | 
| 359 451 | 
             
                 as(:to_sym)
         | 
| 360 452 |  | 
| 361 453 | 
             
            # Yields: Success :a_b
         | 
| @@ -363,35 +455,30 @@ pipe :a, | |
| 363 455 |  | 
| 364 456 | 
             
            ===== Methods
         | 
| 365 457 |  | 
| 366 | 
            -
            Methods  | 
| 458 | 
            +
            Methods, in addition to procs and lambdas, are the _preferred_ way to add custom steps due to the concise syntax. Example:
         | 
| 367 459 |  | 
| 368 460 | 
             
            [source,ruby]
         | 
| 369 461 | 
             
            ----
         | 
| 370 462 | 
             
            class Demo
         | 
| 371 463 | 
             
              include Pipeable
         | 
| 372 464 |  | 
| 373 | 
            -
              def call input
         | 
| 374 | 
            -
                pipe :a,
         | 
| 375 | 
            -
                     insert(:b),
         | 
| 376 | 
            -
                     :join,
         | 
| 377 | 
            -
                     as(:to_sym)
         | 
| 378 | 
            -
              end
         | 
| 465 | 
            +
              def call(input) = pipe input, insert(:b), :join, as(:to_sym)
         | 
| 379 466 |  | 
| 380 467 | 
             
              private
         | 
| 381 468 |  | 
| 382 | 
            -
              def join(result) = result.fmap { | | 
| 469 | 
            +
              def join(result) = result.fmap { |items| items.join "_" }
         | 
| 383 470 | 
             
            end
         | 
| 384 471 |  | 
| 385 | 
            -
            Demo.new.call :a  #  | 
| 472 | 
            +
            Demo.new.call :a  # Success :a_b
         | 
| 386 473 | 
             
            ----
         | 
| 387 474 |  | 
| 388 | 
            -
            All methods can be referenced by symbol as shown via `:join` above. Using a symbol is syntactic sugar for link:https://rubyapi.org/o/object#method-i-method[Object#method] so  | 
| 475 | 
            +
            All methods can be referenced by symbol as shown via `:join` above. Using a symbol is syntactic sugar for link:https://rubyapi.org/o/object#method-i-method[Object#method] so `:join` (symbol) is the same as using `method(:join)`. Both work but the former requires less typing.
         | 
| 389 476 |  | 
| 390 477 | 
             
            ===== Custom
         | 
| 391 478 |  | 
| 392 479 | 
             
            If you'd like to define permanent and reusable steps, you can register a custom step which requires you to:
         | 
| 393 480 |  | 
| 394 | 
            -
            . Define a custom step as a  | 
| 481 | 
            +
            . Define a custom step as a class, lambda, or proc.
         | 
| 395 482 | 
             
            . Register your custom step along side the existing default steps.
         | 
| 396 483 |  | 
| 397 484 | 
             
            Here's what this would look like:
         | 
| @@ -405,7 +492,7 @@ module CustomSteps | |
| 405 492 | 
             
                  @delimiter = delimiter
         | 
| 406 493 | 
             
                end
         | 
| 407 494 |  | 
| 408 | 
            -
                def call(result) = result.fmap { | | 
| 495 | 
            +
                def call(result) = result.fmap { |items| items.join delimiter }
         | 
| 409 496 |  | 
| 410 497 | 
             
                private
         | 
| 411 498 |  | 
| @@ -418,12 +505,82 @@ Pipeable::Steps::Container.register :join, CustomSteps::Join | |
| 418 505 | 
             
            include Pipeable
         | 
| 419 506 |  | 
| 420 507 | 
             
            pipe :a, insert(:b), join, as(:to_sym)
         | 
| 421 | 
            -
            #  | 
| 508 | 
            +
            # Success :a_b
         | 
| 422 509 |  | 
| 423 510 | 
             
            pipe :a, insert(:b), join(""), as(:to_sym)
         | 
| 424 | 
            -
            #  | 
| 511 | 
            +
            # Success :ab
         | 
| 512 | 
            +
            ----
         | 
| 513 | 
            +
             | 
| 514 | 
            +
            A lambda or proc can be used too (albeit in limited capacity). Here's a version of the above using a lambda:
         | 
| 515 | 
            +
             | 
| 516 | 
            +
            [source,ruby]
         | 
| 517 | 
            +
            ----
         | 
| 518 | 
            +
            module CustomSteps
         | 
| 519 | 
            +
              Join = -> result { result.fmap { |items| items.join "_" } }
         | 
| 520 | 
            +
            end
         | 
| 521 | 
            +
             | 
| 522 | 
            +
            Pipeable::Steps::Container.register :join, CustomSteps::Join
         | 
| 523 | 
            +
             | 
| 524 | 
            +
            include Pipeable
         | 
| 525 | 
            +
             | 
| 526 | 
            +
            puts pipe(:a, insert(:b), join, as(:to_sym))
         | 
| 527 | 
            +
            # Success :a_b
         | 
| 425 528 | 
             
            ----
         | 
| 426 529 |  | 
| 530 | 
            +
            === Superpipes
         | 
| 531 | 
            +
             | 
| 532 | 
            +
            Superpipes, as first hinted at in the `use` step above, are a combination of _pipeable_ objects chained together as individual steps. This allows you to reuse existing pipeable objects in new and interesting ways. Here's an contrived, but simple, example of what a superpipe looks like when built from pipeable objects:
         | 
| 533 | 
            +
             | 
| 534 | 
            +
            [source,ruby]
         | 
| 535 | 
            +
            ----
         | 
| 536 | 
            +
            class One
         | 
| 537 | 
            +
              include Pipeable
         | 
| 538 | 
            +
             | 
| 539 | 
            +
              def initialize label = "one"
         | 
| 540 | 
            +
                @label = label
         | 
| 541 | 
            +
              end
         | 
| 542 | 
            +
             | 
| 543 | 
            +
              def call(item) = pipe item, insert(label, at: 0)
         | 
| 544 | 
            +
             | 
| 545 | 
            +
              private
         | 
| 546 | 
            +
             | 
| 547 | 
            +
              attr_reader :label
         | 
| 548 | 
            +
            end
         | 
| 549 | 
            +
             | 
| 550 | 
            +
            class Two
         | 
| 551 | 
            +
              include Pipeable
         | 
| 552 | 
            +
             | 
| 553 | 
            +
              def initialize label = "two"
         | 
| 554 | 
            +
                @label = label
         | 
| 555 | 
            +
              end
         | 
| 556 | 
            +
             | 
| 557 | 
            +
              def call(item) = pipe item, insert(label)
         | 
| 558 | 
            +
             | 
| 559 | 
            +
              private
         | 
| 560 | 
            +
             | 
| 561 | 
            +
              attr_reader :label
         | 
| 562 | 
            +
            end
         | 
| 563 | 
            +
             | 
| 564 | 
            +
            class Three
         | 
| 565 | 
            +
              include Pipeable
         | 
| 566 | 
            +
             | 
| 567 | 
            +
              def initialize one: One.new, two: Two.new
         | 
| 568 | 
            +
                @one = one
         | 
| 569 | 
            +
                @two = two
         | 
| 570 | 
            +
              end
         | 
| 571 | 
            +
             | 
| 572 | 
            +
              def call(item) = pipe item, use(one), use(two)
         | 
| 573 | 
            +
             | 
| 574 | 
            +
              private
         | 
| 575 | 
            +
             | 
| 576 | 
            +
              attr_reader :one, :two
         | 
| 577 | 
            +
            end
         | 
| 578 | 
            +
            ----
         | 
| 579 | 
            +
             | 
| 580 | 
            +
            Notice, `One` and `Two` are standard pipeable objects with their own individual steps while `Three` injects both `One` and `Two` as dependencies and then subsequently pipes them together within it's own `#call` method via the `use` step. This is essence of a superpipe. ...and, yes, a superpipe can be an individual step too in some other object. Turtles all the way down (or up). 😉
         | 
| 581 | 
            +
             | 
| 582 | 
            +
            Again, the above is contrived but hopefully this illustrates how you can build more complex architectures from smaller pipes.
         | 
| 583 | 
            +
             | 
| 427 584 | 
             
            === Containers
         | 
| 428 585 |  | 
| 429 586 | 
             
            Should you not want the basic steps, need custom steps, or a hybrid of default and custom steps, you can define your own container and provide it as an argument to `.[]` when including pipeable behavior. Example:
         | 
| @@ -448,7 +605,7 @@ pipe :a, echo, insert(:b) | |
| 448 605 |  | 
| 449 606 | 
             
            The above is a hybrid example where the `CustomContainer` registers a custom `echo` step along with the default `insert` step to make a new container. This is included when passed in as an argument via `.[]` (i.e. `include Pipeable[CustomContainer]`).
         | 
| 450 607 |  | 
| 451 | 
            -
            Whether you use default, custom, or hybrid steps, you have maximum flexibility using  | 
| 608 | 
            +
            Whether you use default, custom, or hybrid steps, you have maximum flexibility when using containers.
         | 
| 452 609 |  | 
| 453 610 | 
             
            === Composition
         | 
| 454 611 |  | 
| @@ -483,14 +640,14 @@ The architecture of this gem is built on top of the following concepts and gems: | |
| 483 640 |  | 
| 484 641 | 
             
            === Style Guide
         | 
| 485 642 |  | 
| 486 | 
            -
            * * | 
| 487 | 
            -
            ** Use a single method (i.e. `#call`) which is public and adheres to the {command_pattern_link} so  | 
| 643 | 
            +
            * *Pipes*
         | 
| 644 | 
            +
            ** Use a single method (i.e. `#call`) which is public and adheres to the {command_pattern_link} so multiple pipes can be piped together (i.e. superpipes) if desired.
         | 
| 488 645 | 
             
            * *Steps*
         | 
| 489 646 | 
             
            ** Inherit from the `Abstract` class to gain monad, composition, and dependency behavior. This allows subclasses to have direct access to the base positional, keyword, and block arguments. These variables are prefixed with `base_*` in order to not conflict with subclasses which might only want to use non-prefixed variables for convenience.
         | 
| 490 | 
            -
            ** All filtered arguments -- in other words,  | 
| 647 | 
            +
            ** All filtered arguments -- in other words, unused arguments -- need to be passed up to the superclass from the subclass (i.e. `super(*positionals, **keywords, &block)`). Doing so allows the superclass (i.e. `Abstract`) to provide access to `base_positionals`, `base_keywords`, and `base_block` for use if desired by the subclass.
         | 
| 491 648 | 
             
            ** The `#call` method must define a single positional `result` parameter since a monad will be passed as an argument. Example: `def call(result) = # Implementation`.
         | 
| 492 | 
            -
            ** Each block within the `#call` method should use the ` | 
| 493 | 
            -
            ** Use implicit blocks sparingly. Most of the default steps shy away from using blocks because  | 
| 649 | 
            +
            ** Each block within the `#call` method should use the `object` parameter to be consistent. More specific parameters like `operation` or `contract` should be used to improve readability when context allows. Example: `def call(result) = result.bind { |object| # Implementation }`.
         | 
| 650 | 
            +
            ** Use implicit blocks sparingly. Most of the default steps shy away from using blocks because the code becomes more complex. Use private methods, custom steps, and/or separate pipes if the code becomes too complex because you might have a smaller object which needs extraction.
         | 
| 494 651 |  | 
| 495 652 | 
             
            === Debugging
         | 
| 496 653 |  | 
| @@ -508,7 +665,7 @@ The above breakpoint will allow you inspect the result of the `#check` step and/ | |
| 508 665 |  | 
| 509 666 | 
             
            === Troubleshooting
         | 
| 510 667 |  | 
| 511 | 
            -
            The following might be of aid to as you implement your own  | 
| 668 | 
            +
            The following might be of aid to as you implement your own pipes.
         | 
| 512 669 |  | 
| 513 670 | 
             
            ==== Type Errors
         | 
| 514 671 |  | 
| @@ -1,7 +1,6 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 3 | 
             
            require "dry/monads"
         | 
| 4 | 
            -
            require "marameters"
         | 
| 5 4 |  | 
| 6 5 | 
             
            module Pipeable
         | 
| 7 6 | 
             
              module Steps
         | 
| @@ -14,12 +13,11 @@ module Pipeable | |
| 14 13 | 
             
                    @base_positionals = positionals
         | 
| 15 14 | 
             
                    @base_keywords = keywords
         | 
| 16 15 | 
             
                    @base_block = block
         | 
| 17 | 
            -
                    @marameters = Marameters
         | 
| 18 16 | 
             
                  end
         | 
| 19 17 |  | 
| 20 18 | 
             
                  protected
         | 
| 21 19 |  | 
| 22 | 
            -
                  attr_reader :base_positionals, :base_keywords, :base_block | 
| 20 | 
            +
                  attr_reader :base_positionals, :base_keywords, :base_block
         | 
| 23 21 | 
             
                end
         | 
| 24 22 | 
             
              end
         | 
| 25 23 | 
             
            end
         | 
    
        data/lib/pipeable/steps/as.rb
    CHANGED
    
    | @@ -2,10 +2,10 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module Pipeable
         | 
| 4 4 | 
             
              module Steps
         | 
| 5 | 
            -
                #  | 
| 5 | 
            +
                # Messages object, with optional arguments, as different result.
         | 
| 6 6 | 
             
                class As < Abstract
         | 
| 7 7 | 
             
                  def call result
         | 
| 8 | 
            -
                    result.fmap { | | 
| 8 | 
            +
                    result.fmap { |object| object.public_send(*base_positionals, **base_keywords) }
         | 
| 9 9 | 
             
                  end
         | 
| 10 10 | 
             
                end
         | 
| 11 11 | 
             
              end
         | 
    
        data/lib/pipeable/steps/bind.rb
    CHANGED
    
    
    
        data/lib/pipeable/steps/check.rb
    CHANGED
    
    | @@ -1,29 +1,31 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            +
            require "marameters"
         | 
| 4 | 
            +
             | 
| 3 5 | 
             
            module Pipeable
         | 
| 4 6 | 
             
              module Steps
         | 
| 5 | 
            -
                # Checks if  | 
| 7 | 
            +
                # Checks if proof is true and answers success (passthrough) or failure (with optional argument).
         | 
| 6 8 | 
             
                class Check < Abstract
         | 
| 7 | 
            -
                  def initialize | 
| 8 | 
            -
                    super( | 
| 9 | 
            -
                    @ | 
| 9 | 
            +
                  def initialize proof, message
         | 
| 10 | 
            +
                    super()
         | 
| 11 | 
            +
                    @proof = proof
         | 
| 10 12 | 
             
                    @message = message
         | 
| 11 13 | 
             
                  end
         | 
| 12 14 |  | 
| 13 15 | 
             
                  def call result
         | 
| 14 | 
            -
                    result.bind do | | 
| 15 | 
            -
                      answer = question  | 
| 16 | 
            -
                      answer == true || answer.is_a?(Success) ? result : Failure( | 
| 16 | 
            +
                    result.bind do |object|
         | 
| 17 | 
            +
                      answer = question object
         | 
| 18 | 
            +
                      answer == true || answer.is_a?(Success) ? result : Failure(object)
         | 
| 17 19 | 
             
                    end
         | 
| 18 20 | 
             
                  end
         | 
| 19 21 |  | 
| 20 22 | 
             
                  private
         | 
| 21 23 |  | 
| 22 | 
            -
                  attr_reader : | 
| 24 | 
            +
                  attr_reader :proof, :message
         | 
| 23 25 |  | 
| 24 | 
            -
                  def question  | 
| 25 | 
            -
                    splat =  | 
| 26 | 
            -
                     | 
| 26 | 
            +
                  def question object
         | 
| 27 | 
            +
                    splat = Marameters.categorize proof.method(message).parameters, object
         | 
| 28 | 
            +
                    proof.public_send(message, *splat.positionals, **splat.keywords, &splat.block)
         | 
| 27 29 | 
             
                  end
         | 
| 28 30 | 
             
                end
         | 
| 29 31 | 
             
              end
         | 
| @@ -8,6 +8,7 @@ module Pipeable | |
| 8 8 | 
             
                module Container
         | 
| 9 9 | 
             
                  extend Containable
         | 
| 10 10 |  | 
| 11 | 
            +
                  register :alt, Or
         | 
| 11 12 | 
             
                  register :as, As
         | 
| 12 13 | 
             
                  register :bind, Bind
         | 
| 13 14 | 
             
                  register :check, Check
         | 
| @@ -15,7 +16,6 @@ module Pipeable | |
| 15 16 | 
             
                  register :insert, Insert
         | 
| 16 17 | 
             
                  register :map, Map
         | 
| 17 18 | 
             
                  register :merge, Merge
         | 
| 18 | 
            -
                  register :orr, Or
         | 
| 19 19 | 
             
                  register :tee, Tee
         | 
| 20 20 | 
             
                  register :to, To
         | 
| 21 21 | 
             
                  register :try, Try
         | 
    
        data/lib/pipeable/steps/fmap.rb
    CHANGED
    
    
| @@ -2,7 +2,7 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module Pipeable
         | 
| 4 4 | 
             
              module Steps
         | 
| 5 | 
            -
                # Inserts elements before | 
| 5 | 
            +
                # Inserts elements before or after an object.
         | 
| 6 6 | 
             
                class Insert < Abstract
         | 
| 7 7 | 
             
                  LAST = -1
         | 
| 8 8 |  | 
| @@ -13,8 +13,8 @@ module Pipeable | |
| 13 13 | 
             
                  end
         | 
| 14 14 |  | 
| 15 15 | 
             
                  def call result
         | 
| 16 | 
            -
                    result.fmap do | | 
| 17 | 
            -
                      cast =  | 
| 16 | 
            +
                    result.fmap do |object|
         | 
| 17 | 
            +
                      cast = object.is_a?(Array) ? object : [object]
         | 
| 18 18 | 
             
                      value.is_a?(Array) ? cast.insert(at, *value) : cast.insert(at, value)
         | 
| 19 19 | 
             
                    end
         | 
| 20 20 | 
             
                  end
         | 
    
        data/lib/pipeable/steps/map.rb
    CHANGED
    
    | @@ -2,7 +2,7 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module Pipeable
         | 
| 4 4 | 
             
              module Steps
         | 
| 5 | 
            -
                # Maps over  | 
| 5 | 
            +
                # Maps over an enumerable, processes each element, and answers a new enumerable.
         | 
| 6 6 | 
             
                class Map < Abstract
         | 
| 7 7 | 
             
                  def call(result) = result.fmap { |collection| collection.map(&base_block) }
         | 
| 8 8 | 
             
                end
         | 
    
        data/lib/pipeable/steps/merge.rb
    CHANGED
    
    | @@ -2,19 +2,19 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module Pipeable
         | 
| 4 4 | 
             
              module Steps
         | 
| 5 | 
            -
                # Merges initialized attributes with step  | 
| 5 | 
            +
                # Merges initialized attributes with step object for use by subsequent step.
         | 
| 6 6 | 
             
                class Merge < Abstract
         | 
| 7 | 
            -
                  def initialize | 
| 8 | 
            -
                    super(** | 
| 7 | 
            +
                  def initialize(as: :step, **)
         | 
| 8 | 
            +
                    super(**)
         | 
| 9 9 | 
             
                    @as = as
         | 
| 10 10 | 
             
                  end
         | 
| 11 11 |  | 
| 12 12 | 
             
                  def call result
         | 
| 13 | 
            -
                    result.fmap do | | 
| 14 | 
            -
                      if  | 
| 15 | 
            -
                         | 
| 13 | 
            +
                    result.fmap do |object|
         | 
| 14 | 
            +
                      if object.is_a? Hash
         | 
| 15 | 
            +
                        object.merge! base_keywords
         | 
| 16 16 | 
             
                      else
         | 
| 17 | 
            -
                        {as =>  | 
| 17 | 
            +
                        {as => object}.merge!(base_keywords)
         | 
| 18 18 | 
             
                      end
         | 
| 19 19 | 
             
                    end
         | 
| 20 20 | 
             
                  end
         | 
    
        data/lib/pipeable/steps/or.rb
    CHANGED
    
    
    
        data/lib/pipeable/steps/tee.rb
    CHANGED
    
    | @@ -2,7 +2,7 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module Pipeable
         | 
| 4 4 | 
             
              module Steps
         | 
| 5 | 
            -
                # Messages operation, without any  | 
| 5 | 
            +
                # Messages operation, without any checks, while passing input through as output.
         | 
| 6 6 | 
             
                class Tee < Abstract
         | 
| 7 7 | 
             
                  def initialize(operation, *, **)
         | 
| 8 8 | 
             
                    super(*, **)
         | 
    
        data/lib/pipeable/steps/to.rb
    CHANGED
    
    | @@ -1,25 +1,27 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            +
            require "marameters"
         | 
| 4 | 
            +
             | 
| 3 5 | 
             
            module Pipeable
         | 
| 4 6 | 
             
              module Steps
         | 
| 5 | 
            -
                # Delegates to a non-callable  | 
| 7 | 
            +
                # Delegates to a non-callable object which automatically wraps the result if necessary.
         | 
| 6 8 | 
             
                class To < Abstract
         | 
| 7 | 
            -
                  def initialize( | 
| 9 | 
            +
                  def initialize(object, message, **)
         | 
| 8 10 | 
             
                    super(**)
         | 
| 9 | 
            -
                    @ | 
| 11 | 
            +
                    @object = object
         | 
| 10 12 | 
             
                    @message = message
         | 
| 11 13 | 
             
                  end
         | 
| 12 14 |  | 
| 13 15 | 
             
                  def call result
         | 
| 14 16 | 
             
                    result.bind do |arguments|
         | 
| 15 | 
            -
                      splat =  | 
| 16 | 
            -
                      wrap  | 
| 17 | 
            +
                      splat = Marameters.categorize object.method(message).parameters, arguments
         | 
| 18 | 
            +
                      wrap object.public_send(message, *splat.positionals, **splat.keywords, &splat.block)
         | 
| 17 19 | 
             
                    end
         | 
| 18 20 | 
             
                  end
         | 
| 19 21 |  | 
| 20 22 | 
             
                  private
         | 
| 21 23 |  | 
| 22 | 
            -
                  attr_reader : | 
| 24 | 
            +
                  attr_reader :object, :message
         | 
| 23 25 |  | 
| 24 26 | 
             
                  def wrap(result) = result.is_a?(Dry::Monads::Result) ? result : Success(result)
         | 
| 25 27 | 
             
                end
         | 
    
        data/lib/pipeable/steps/try.rb
    CHANGED
    
    | @@ -2,17 +2,17 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module Pipeable
         | 
| 4 4 | 
             
              module Steps
         | 
| 5 | 
            -
                #  | 
| 5 | 
            +
                # Sends a risky message to an object which may pass or fail.
         | 
| 6 6 | 
             
                class Try < Abstract
         | 
| 7 | 
            -
                  def initialize  | 
| 8 | 
            -
                    super( | 
| 7 | 
            +
                  def initialize(*, catch:, **)
         | 
| 8 | 
            +
                    super(*, **)
         | 
| 9 9 | 
             
                    @catch = catch
         | 
| 10 10 | 
             
                  end
         | 
| 11 11 |  | 
| 12 12 | 
             
                  def call result
         | 
| 13 | 
            -
                    result.fmap { | | 
| 13 | 
            +
                    result.fmap { |object| object.public_send(*base_positionals, **base_keywords) }
         | 
| 14 14 | 
             
                  rescue *Array(catch) => error
         | 
| 15 | 
            -
                    Failure error | 
| 15 | 
            +
                    Failure error
         | 
| 16 16 | 
             
                  end
         | 
| 17 17 |  | 
| 18 18 | 
             
                  private
         | 
    
        data/lib/pipeable/steps/use.rb
    CHANGED
    
    | @@ -2,18 +2,18 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module Pipeable
         | 
| 4 4 | 
             
              module Steps
         | 
| 5 | 
            -
                #  | 
| 5 | 
            +
                # Messages a command (or pipe) which answers a result.
         | 
| 6 6 | 
             
                class Use < Abstract
         | 
| 7 | 
            -
                  def initialize( | 
| 7 | 
            +
                  def initialize(command, **)
         | 
| 8 8 | 
             
                    super(**)
         | 
| 9 | 
            -
                    @ | 
| 9 | 
            +
                    @command = command
         | 
| 10 10 | 
             
                  end
         | 
| 11 11 |  | 
| 12 | 
            -
                  def call(result) = result.bind { |input|  | 
| 12 | 
            +
                  def call(result) = result.bind { |input| command.call input }
         | 
| 13 13 |  | 
| 14 14 | 
             
                  private
         | 
| 15 15 |  | 
| 16 | 
            -
                  attr_reader : | 
| 16 | 
            +
                  attr_reader :command
         | 
| 17 17 | 
             
                end
         | 
| 18 18 | 
             
              end
         | 
| 19 19 | 
             
            end
         | 
| @@ -2,27 +2,23 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module Pipeable
         | 
| 4 4 | 
             
              module Steps
         | 
| 5 | 
            -
                # Validates  | 
| 5 | 
            +
                # Validates result via a callable contract.
         | 
| 6 6 | 
             
                class Validate < Abstract
         | 
| 7 | 
            -
                  def initialize | 
| 8 | 
            -
                    super( | 
| 9 | 
            -
                    @ | 
| 7 | 
            +
                  def initialize contract, as: nil
         | 
| 8 | 
            +
                    super()
         | 
| 9 | 
            +
                    @contract = contract
         | 
| 10 10 | 
             
                    @as = as
         | 
| 11 11 | 
             
                  end
         | 
| 12 12 |  | 
| 13 | 
            -
                  def call result
         | 
| 14 | 
            -
                    result.bind do |payload|
         | 
| 15 | 
            -
                      value = operation.call payload
         | 
| 16 | 
            -
             | 
| 17 | 
            -
                      return Failure value if value.failure?
         | 
| 18 | 
            -
             | 
| 19 | 
            -
                      Success(as ? value.public_send(as) : value)
         | 
| 20 | 
            -
                    end
         | 
| 21 | 
            -
                  end
         | 
| 13 | 
            +
                  def call(result) = result.bind { |payload| cast payload }
         | 
| 22 14 |  | 
| 23 15 | 
             
                  private
         | 
| 24 16 |  | 
| 25 | 
            -
                  attr_reader : | 
| 17 | 
            +
                  attr_reader :contract, :as
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  def cast payload
         | 
| 20 | 
            +
                    contract.call(payload).to_monad.fmap { |data| as ? data.public_send(as) : data }
         | 
| 21 | 
            +
                  end
         | 
| 26 22 | 
             
                end
         | 
| 27 23 | 
             
              end
         | 
| 28 24 | 
             
            end
         | 
    
        data/pipeable.gemspec
    CHANGED
    
    
    
        data.tar.gz.sig
    CHANGED
    
    | Binary file | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: pipeable
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.5.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Brooke Kuhlmann
         | 
| @@ -35,7 +35,7 @@ cert_chain: | |
| 35 35 | 
             
              3n5C8/6Zh9DYTkpcwPSuIfAga6wf4nXc9m6JAw8AuMLaiWN/r/2s4zJsUHYERJEu
         | 
| 36 36 | 
             
              gZGm4JqtuSg8pYjPeIJxS960owq+SfuC+jxqmRA54BisFCv/0VOJi7tiJVY=
         | 
| 37 37 | 
             
              -----END CERTIFICATE-----
         | 
| 38 | 
            -
            date: 2024- | 
| 38 | 
            +
            date: 2024-05-07 00:00:00.000000000 Z
         | 
| 39 39 | 
             
            dependencies:
         | 
| 40 40 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 41 41 | 
             
              name: containable
         | 
| @@ -164,7 +164,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 164 164 | 
             
                - !ruby/object:Gem::Version
         | 
| 165 165 | 
             
                  version: '0'
         | 
| 166 166 | 
             
            requirements: []
         | 
| 167 | 
            -
            rubygems_version: 3.5. | 
| 167 | 
            +
            rubygems_version: 3.5.10
         | 
| 168 168 | 
             
            signing_key:
         | 
| 169 169 | 
             
            specification_version: 4
         | 
| 170 170 | 
             
            summary: A domain specific language for building functionally composable steps.
         | 
    
        metadata.gz.sig
    CHANGED
    
    | Binary file |