deterministic 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/README.md +17 -3
 - data/lib/deterministic.rb +2 -0
 - data/lib/deterministic/either.rb +14 -8
 - data/lib/deterministic/either/attempt_all.rb +4 -2
 - data/lib/deterministic/either/failure.rb +3 -1
 - data/lib/deterministic/either/match.rb +12 -14
 - data/lib/deterministic/either/success.rb +3 -1
 - data/lib/deterministic/monad.rb +10 -7
 - data/lib/deterministic/version.rb +1 -1
 - data/spec/examples/bookings_spec.rb +74 -0
 - data/spec/examples/validate_address_spec.rb +31 -0
 - data/spec/lib/deterministic/attempt_all_spec.rb +14 -6
 - data/spec/lib/deterministic/either/match_spec.rb +15 -1
 - data/spec/lib/deterministic/either/success_spec.rb +0 -4
 - data/spec/lib/deterministic/either_spec.rb +8 -0
 - data/spec/lib/deterministic/monad_spec.rb +22 -2
 - data/spec/spec_helper.rb +1 -0
 - metadata +9 -3
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA1:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 24278b8af8e996a30226c448b0677f8669e14606
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 6a67ad79ac2e6ff81e0530b547c658f0cfb4633a
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 0fd77c14af51a1814426d1c33bffccda8b97a58c5266638503dca676e1cbd78ac8a159becabfb3e4fe262016b5a2d0bf1663111f76d0fcbe92e7d105fa296f52
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 6181518d77416ec8cf02712cd738f56b842deaf212a4ac368b8babcc141c01bf718fb7b9b10f2d11d6109cf36a35db0b3306fce8629cdafca940d239d9a2c675
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -1,6 +1,6 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # Deterministic
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            This is a spiritual successor of the [Monadic gem](http://github.com/pzol/monadic). 
         
     | 
| 
      
 3 
     | 
    
         
            +
            This is a spiritual successor of the [Monadic gem](http://github.com/pzol/monadic). The goal of the rewrite is to get away from a bit to forceful aproach I took in Monadic, especially when it comes to coercing monads, but also a more practical but at the same time more strict adherence to monad laws.
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         
             
            This gem is still __WORK IN PROGRESS__.
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
         @@ -60,6 +60,12 @@ However, the real fun starts if you use it with your own context. You can use th 
     | 
|
| 
       60 
60 
     | 
    
         | 
| 
       61 
61 
     | 
    
         
             
            ### Pattern matching
         
     | 
| 
       62 
62 
     | 
    
         
             
            Now that you have some result, you want to control flow by providing patterns.
         
     | 
| 
      
 63 
     | 
    
         
            +
            `#match` can match by
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
             * success, failure, either or any
         
     | 
| 
      
 66 
     | 
    
         
            +
             * values
         
     | 
| 
      
 67 
     | 
    
         
            +
             * lambdas
         
     | 
| 
      
 68 
     | 
    
         
            +
             * classes
         
     | 
| 
       63 
69 
     | 
    
         | 
| 
       64 
70 
     | 
    
         
             
            ```ruby
         
     | 
| 
       65 
71 
     | 
    
         
             
            Success(1).match do
         
     | 
| 
         @@ -70,9 +76,9 @@ end # => "either 1" 
     | 
|
| 
       70 
76 
     | 
    
         
             
            ```
         
     | 
| 
       71 
77 
     | 
    
         
             
            Note1: the inner value has been unwrapped! 
         
     | 
| 
       72 
78 
     | 
    
         | 
| 
       73 
     | 
    
         
            -
            Note2: only the  
     | 
| 
      
 79 
     | 
    
         
            +
            Note2: only the __first__ matching pattern block will be executed, so order __can__ be important.
         
     | 
| 
       74 
80 
     | 
    
         | 
| 
       75 
     | 
    
         
            -
            The result returned will be the result of the  
     | 
| 
      
 81 
     | 
    
         
            +
            The result returned will be the result of the __first__ `#try` or `#let`. As a side note, `#try` is a monad, `#let` is a functor.
         
     | 
| 
       76 
82 
     | 
    
         | 
| 
       77 
83 
     | 
    
         
             
            Values for patterns are good, too:
         
     | 
| 
       78 
84 
     | 
    
         | 
| 
         @@ -90,6 +96,14 @@ Success(1).match do 
     | 
|
| 
       90 
96 
     | 
    
         
             
            end # => "Success 1"
         
     | 
| 
       91 
97 
     | 
    
         
             
            ```
         
     | 
| 
       92 
98 
     | 
    
         | 
| 
      
 99 
     | 
    
         
            +
            Also you can match the result class
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 102 
     | 
    
         
            +
            Success([1, 2, 3]).match do
         
     | 
| 
      
 103 
     | 
    
         
            +
              success(Array) { |v| v.first }
         
     | 
| 
      
 104 
     | 
    
         
            +
            end # => 1
         
     | 
| 
      
 105 
     | 
    
         
            +
            ```
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
       93 
107 
     | 
    
         
             
            Combining `#attempt_all` and `#match` is the ultimate sophistication:
         
     | 
| 
       94 
108 
     | 
    
         | 
| 
       95 
109 
     | 
    
         
             
            ```ruby
         
     | 
    
        data/lib/deterministic.rb
    CHANGED
    
    
    
        data/lib/deterministic/either.rb
    CHANGED
    
    | 
         @@ -1,12 +1,4 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module Deterministic
         
     | 
| 
       2 
     | 
    
         
            -
              def Success(value)
         
     | 
| 
       3 
     | 
    
         
            -
                Success.new(value)
         
     | 
| 
       4 
     | 
    
         
            -
              end
         
     | 
| 
       5 
     | 
    
         
            -
             
     | 
| 
       6 
     | 
    
         
            -
              def Failure(value)
         
     | 
| 
       7 
     | 
    
         
            -
                Failure.new(value)
         
     | 
| 
       8 
     | 
    
         
            -
              end
         
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
       10 
2 
     | 
    
         
             
              class Either
         
     | 
| 
       11 
3 
     | 
    
         
             
                include Monad
         
     | 
| 
       12 
4 
     | 
    
         
             
                include Deterministic::PatternMatching
         
     | 
| 
         @@ -23,5 +15,19 @@ module Deterministic 
     | 
|
| 
       23 
15 
     | 
    
         
             
                  return self if failure?
         
     | 
| 
       24 
16 
     | 
    
         
             
                  return other if other.is_a? Either
         
     | 
| 
       25 
17 
     | 
    
         
             
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 20 
     | 
    
         
            +
                  protected :new
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            module_function
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
              def Success(value)
         
     | 
| 
      
 27 
     | 
    
         
            +
                Success.new(value)
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
              def Failure(value)
         
     | 
| 
      
 31 
     | 
    
         
            +
                Failure.new(value)
         
     | 
| 
       26 
32 
     | 
    
         
             
              end
         
     | 
| 
       27 
33 
     | 
    
         
             
            end
         
     | 
| 
         @@ -19,6 +19,7 @@ class Deterministic::Either 
     | 
|
| 
       19 
19 
     | 
    
         
             
                  end
         
     | 
| 
       20 
20 
     | 
    
         
             
                end
         
     | 
| 
       21 
21 
     | 
    
         | 
| 
      
 22 
     | 
    
         
            +
                # This is a functor
         
     | 
| 
       22 
23 
     | 
    
         
             
                def try(&block)
         
     | 
| 
       23 
24 
     | 
    
         
             
                  try_p = ->(acc) {
         
     | 
| 
       24 
25 
     | 
    
         
             
                    begin
         
     | 
| 
         @@ -32,10 +33,11 @@ class Deterministic::Either 
     | 
|
| 
       32 
33 
     | 
    
         
             
                  @tries << try_p
         
     | 
| 
       33 
34 
     | 
    
         
             
                end
         
     | 
| 
       34 
35 
     | 
    
         | 
| 
      
 36 
     | 
    
         
            +
                # Basicly a monad
         
     | 
| 
       35 
37 
     | 
    
         
             
                def let(sym=nil, &block)
         
     | 
| 
       36 
38 
     | 
    
         
             
                  @tries << ->(acc) { 
         
     | 
| 
       37 
     | 
    
         
            -
                    @context.instance_exec(acc, &block).tap do |value|
         
     | 
| 
       38 
     | 
    
         
            -
                      raise EitherExpectedError unless value.is_a? Either
         
     | 
| 
      
 39 
     | 
    
         
            +
                    @context.instance_exec(acc.value, &block).tap do |value|
         
     | 
| 
      
 40 
     | 
    
         
            +
                      raise EitherExpectedError, "Expected the result to be either Success or Failure" unless value.is_a? Either
         
     | 
| 
       39 
41 
     | 
    
         
             
                    end
         
     | 
| 
       40 
42 
     | 
    
         
             
                  }
         
     | 
| 
       41 
43 
     | 
    
         
             
                end
         
     | 
| 
         @@ -1,23 +1,24 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module Deterministic::PatternMatching
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
              def match( 
     | 
| 
       4 
     | 
    
         
            -
                match = Match.new(self)
         
     | 
| 
       5 
     | 
    
         
            -
                match.instance_eval & 
     | 
| 
      
 3 
     | 
    
         
            +
              def match(context=self, &block)
         
     | 
| 
      
 4 
     | 
    
         
            +
                match = Match.new(self, context)
         
     | 
| 
      
 5 
     | 
    
         
            +
                match.instance_eval &block
         
     | 
| 
       6 
6 
     | 
    
         
             
                match.result
         
     | 
| 
       7 
7 
     | 
    
         
             
              end
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
9 
     | 
    
         
             
              class NoMatchError < StandardError; end
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
       11 
11 
     | 
    
         
             
              class Match
         
     | 
| 
       12 
     | 
    
         
            -
                def initialize(container)
         
     | 
| 
      
 12 
     | 
    
         
            +
                def initialize(container, context)
         
     | 
| 
       13 
13 
     | 
    
         
             
                  @container  = container
         
     | 
| 
      
 14 
     | 
    
         
            +
                  @context    = context
         
     | 
| 
       14 
15 
     | 
    
         
             
                  @collection = []
         
     | 
| 
       15 
16 
     | 
    
         
             
                end
         
     | 
| 
       16 
17 
     | 
    
         | 
| 
       17 
18 
     | 
    
         
             
                def result
         
     | 
| 
       18 
     | 
    
         
            -
                  matcher = @collection. 
     | 
| 
       19 
     | 
    
         
            -
                  raise NoMatchError if matcher.nil?
         
     | 
| 
       20 
     | 
    
         
            -
                   
     | 
| 
      
 19 
     | 
    
         
            +
                  matcher = @collection.detect { |m| m.matches?(@container.value) }
         
     | 
| 
      
 20 
     | 
    
         
            +
                  raise NoMatchError, "No could be made for #{@container}" if matcher.nil?
         
     | 
| 
      
 21 
     | 
    
         
            +
                  @context.instance_exec(@container.value, &matcher.block)
         
     | 
| 
       21 
22 
     | 
    
         
             
                end
         
     | 
| 
       22 
23 
     | 
    
         | 
| 
       23 
24 
     | 
    
         
             
                # TODO: Either specific DSL, will need to move it to Either, later on
         
     | 
| 
         @@ -38,17 +39,14 @@ module Deterministic::PatternMatching 
     | 
|
| 
       38 
39 
     | 
    
         
             
                  def matches?(value)
         
     | 
| 
       39 
40 
     | 
    
         
             
                    condition.call(value)
         
     | 
| 
       40 
41 
     | 
    
         
             
                  end
         
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
     | 
    
         
            -
                  def result(value)
         
     | 
| 
       43 
     | 
    
         
            -
                    block.call(value)
         
     | 
| 
       44 
     | 
    
         
            -
                  end
         
     | 
| 
       45 
42 
     | 
    
         
             
                end
         
     | 
| 
       46 
43 
     | 
    
         | 
| 
       47 
44 
     | 
    
         
             
                def push(type, condition, result_block)
         
     | 
| 
       48 
45 
     | 
    
         
             
                  condition_pred = case
         
     | 
| 
       49 
     | 
    
         
            -
                  when condition.nil?; 
     | 
| 
       50 
     | 
    
         
            -
                  when condition.is_a?(Proc); 
     | 
| 
       51 
     | 
    
         
            -
                   
     | 
| 
      
 46 
     | 
    
         
            +
                  when condition.nil?;          ->(v) { true }
         
     | 
| 
      
 47 
     | 
    
         
            +
                  when condition.is_a?(Proc);   condition
         
     | 
| 
      
 48 
     | 
    
         
            +
                  when condition.is_a?(Class);  ->(v) { condition === @container.value }
         
     | 
| 
      
 49 
     | 
    
         
            +
                  else                          ->(v) { condition == @container.value }
         
     | 
| 
       52 
50 
     | 
    
         
             
                  end
         
     | 
| 
       53 
51 
     | 
    
         | 
| 
       54 
52 
     | 
    
         
             
                  matcher_pred = compose_predicates(type_pred[type], condition_pred)
         
     | 
    
        data/lib/deterministic/monad.rb
    CHANGED
    
    | 
         @@ -15,26 +15,29 @@ module Deterministic 
     | 
|
| 
       15 
15 
     | 
    
         | 
| 
       16 
16 
     | 
    
         
             
                # The functor: takes a function (a -> b) and applies it to the inner value of the monad (Ma),
         
     | 
| 
       17 
17 
     | 
    
         
             
                # boxes it back to the same monad (Mb)
         
     | 
| 
       18 
     | 
    
         
            -
                # fmap :: (a -> b) ->  
     | 
| 
      
 18 
     | 
    
         
            +
                # fmap :: (a -> b) -> M a -> M b
         
     | 
| 
       19 
19 
     | 
    
         
             
                def map(proc=nil, &block)
         
     | 
| 
       20 
20 
     | 
    
         
             
                  result = (proc || block).call(value)
         
     | 
| 
       21 
21 
     | 
    
         
             
                  self.class.new(result)
         
     | 
| 
       22 
22 
     | 
    
         
             
                end
         
     | 
| 
       23 
23 
     | 
    
         | 
| 
       24 
     | 
    
         
            -
                # The monad: takes a function which returns a monad, applies
         
     | 
| 
       25 
     | 
    
         
            -
                # bind ::  
     | 
| 
      
 24 
     | 
    
         
            +
                # The monad: takes a function which returns a monad (of the same type), applies the function
         
     | 
| 
      
 25 
     | 
    
         
            +
                # bind :: M a  -> (a -> Mb) -> M b
         
     | 
| 
      
 26 
     | 
    
         
            +
                # the self.class, i.e. the containing monad is passed as a second (optional) arg to the function
         
     | 
| 
       26 
27 
     | 
    
         
             
                def bind(proc=nil, &block)
         
     | 
| 
       27 
     | 
    
         
            -
                   
     | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
       29 
     | 
    
         
            -
                   
     | 
| 
      
 28 
     | 
    
         
            +
                  (proc || block).call(value, self.class).tap do |result|
         
     | 
| 
      
 29 
     | 
    
         
            +
                    raise NotMonadError unless result.is_a? self.class
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
       30 
31 
     | 
    
         
             
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
                alias :'>>=' :bind
         
     | 
| 
       31 
33 
     | 
    
         | 
| 
       32 
34 
     | 
    
         
             
                # Get the underlying value, return in Haskell
         
     | 
| 
       33 
     | 
    
         
            -
                # return ::  
     | 
| 
      
 35 
     | 
    
         
            +
                # return :: M a -> a
         
     | 
| 
       34 
36 
     | 
    
         
             
                def value
         
     | 
| 
       35 
37 
     | 
    
         
             
                  @value
         
     | 
| 
       36 
38 
     | 
    
         
             
                end
         
     | 
| 
       37 
39 
     | 
    
         | 
| 
      
 40 
     | 
    
         
            +
                # Two monads are equivalent if they are of the same type and when their values are equal
         
     | 
| 
       38 
41 
     | 
    
         
             
                def ==(other)
         
     | 
| 
       39 
42 
     | 
    
         
             
                  return false unless other.is_a? self.class
         
     | 
| 
       40 
43 
     | 
    
         
             
                  @value == other.instance_variable_get(:@value)
         
     | 
| 
         @@ -0,0 +1,74 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'deterministic'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            include Deterministic
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module Examples
         
     | 
| 
      
 7 
     | 
    
         
            +
              module Bookings
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                module Adapters
         
     | 
| 
      
 10 
     | 
    
         
            +
                  class BookingsRepo
         
     | 
| 
      
 11 
     | 
    
         
            +
                    def find(params)
         
     | 
| 
      
 12 
     | 
    
         
            +
                    end
         
     | 
| 
      
 13 
     | 
    
         
            +
                  end
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                class FakeWebUi
         
     | 
| 
      
 17 
     | 
    
         
            +
                  def method_missing(m, *args)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    { m => args }
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                class Dependencies
         
     | 
| 
      
 23 
     | 
    
         
            +
                  include Adapters
         
     | 
| 
      
 24 
     | 
    
         
            +
                  def bookings_repo
         
     | 
| 
      
 25 
     | 
    
         
            +
                    BookingsRepo.new
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                class ShowBookings
         
     | 
| 
      
 30 
     | 
    
         
            +
                  def initialize(world=FakeWebUi.new, deps=Dependencies.new)
         
     | 
| 
      
 31 
     | 
    
         
            +
                    @world         = world
         
     | 
| 
      
 32 
     | 
    
         
            +
                    @world.booking_list([])
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                  def call(dirty_params)
         
     | 
| 
      
 36 
     | 
    
         
            +
                    result = Either.attempt_all(self) do
         
     | 
| 
      
 37 
     | 
    
         
            +
                      try {          parse_params(dirty_params) }
         
     | 
| 
      
 38 
     | 
    
         
            +
                      let { |params| read_bookings(params)           }
         
     | 
| 
      
 39 
     | 
    
         
            +
                    end.match(world) do
         
     | 
| 
      
 40 
     | 
    
         
            +
                      success(Array)         { |bookings| booking_list(bookings) }
         
     | 
| 
      
 41 
     | 
    
         
            +
                      success                { |booking|  booking(booking)       }
         
     | 
| 
      
 42 
     | 
    
         
            +
                      failure(:no_bookings)  { empty_booking_list                }
         
     | 
| 
      
 43 
     | 
    
         
            +
                      failure(StandardError) { |ex| raise ex }
         
     | 
| 
      
 44 
     | 
    
         
            +
                      any                    { |result| raise "Something went terribly wrong `#{result.class}`" }
         
     | 
| 
      
 45 
     | 
    
         
            +
                    end
         
     | 
| 
      
 46 
     | 
    
         
            +
                  end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                # private
         
     | 
| 
      
 49 
     | 
    
         
            +
                  attr_reader   :bookings_repo, :world
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                  def parse_params(dirty_params)
         
     | 
| 
      
 52 
     | 
    
         
            +
                    dirty_params
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  def read_bookings(params)
         
     | 
| 
      
 56 
     | 
    
         
            +
                    bookings = [params]
         
     | 
| 
      
 57 
     | 
    
         
            +
                    case bookings
         
     | 
| 
      
 58 
     | 
    
         
            +
                      when nil; Failure(:no_bookings)
         
     | 
| 
      
 59 
     | 
    
         
            +
                      when bookings.count == 1; 
         
     | 
| 
      
 60 
     | 
    
         
            +
                      else Success(bookings)
         
     | 
| 
      
 61 
     | 
    
         
            +
                    end
         
     | 
| 
      
 62 
     | 
    
         
            +
                  end
         
     | 
| 
      
 63 
     | 
    
         
            +
                end
         
     | 
| 
      
 64 
     | 
    
         
            +
              end
         
     | 
| 
      
 65 
     | 
    
         
            +
            end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
            describe Examples::Bookings::ShowBookings do
         
     | 
| 
      
 68 
     | 
    
         
            +
              subject { described_class.new }
         
     | 
| 
      
 69 
     | 
    
         
            +
              it "works" do
         
     | 
| 
      
 70 
     | 
    
         
            +
                expect(
         
     | 
| 
      
 71 
     | 
    
         
            +
                  subject.call({status: 'confirmed'})
         
     | 
| 
      
 72 
     | 
    
         
            +
                ).to eq({:booking_list=>[[{:status=>"confirmed"}]]})
         
     | 
| 
      
 73 
     | 
    
         
            +
              end
         
     | 
| 
      
 74 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,31 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'deterministic'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            include Deterministic
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            # A Unit of Work for validating an address
         
     | 
| 
      
 7 
     | 
    
         
            +
            module ValidateAddress
         
     | 
| 
      
 8 
     | 
    
         
            +
              def self.call(candidate)
         
     | 
| 
      
 9 
     | 
    
         
            +
                errors = {}
         
     | 
| 
      
 10 
     | 
    
         
            +
                errors[:street] = "Street cannot be empty" unless candidate.has_key? :street
         
     | 
| 
      
 11 
     | 
    
         
            +
                errors[:city]   = "Street cannot be empty" unless candidate.has_key? :city
         
     | 
| 
      
 12 
     | 
    
         
            +
                errors[:postal] = "Street cannot be empty" unless candidate.has_key? :postal
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                errors.empty? ? Success(candidate) : Failure(errors)
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
      
 16 
     | 
    
         
            +
            end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            describe ValidateAddress do
         
     | 
| 
      
 19 
     | 
    
         
            +
              subject { ValidateAddress.call(candidate)  }
         
     | 
| 
      
 20 
     | 
    
         
            +
              context 'sunny day' do
         
     | 
| 
      
 21 
     | 
    
         
            +
                let(:candidate) { {title: "Hobbiton", street: "501 Buckland Rd", city: "Matamata", postal: "3472", country: "nz"} }
         
     | 
| 
      
 22 
     | 
    
         
            +
                specify { expect(subject).to be_a Success }
         
     | 
| 
      
 23 
     | 
    
         
            +
                specify { expect(subject.value).to eq candidate }
         
     | 
| 
      
 24 
     | 
    
         
            +
              end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
              context 'empty data' do
         
     | 
| 
      
 27 
     | 
    
         
            +
                let(:candidate) { {} }
         
     | 
| 
      
 28 
     | 
    
         
            +
                specify { expect(subject).to be_a Failure }
         
     | 
| 
      
 29 
     | 
    
         
            +
                specify { expect(subject.value).to include(:street, :city, :postal) }
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -1,5 +1,4 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            require 'spec_helper'
         
     | 
| 
       2 
     | 
    
         
            -
            require 'deterministic'
         
     | 
| 
       3 
2 
     | 
    
         | 
| 
       4 
3 
     | 
    
         
             
            include Deterministic
         
     | 
| 
       5 
4 
     | 
    
         | 
| 
         @@ -64,6 +63,15 @@ describe Deterministic::Either::AttemptAll do 
     | 
|
| 
       64 
63 
     | 
    
         
             
                }.to raise_error
         
     | 
| 
       65 
64 
     | 
    
         
             
              end
         
     | 
| 
       66 
65 
     | 
    
         | 
| 
      
 66 
     | 
    
         
            +
              it "#let passes params unboxed" do
         
     | 
| 
      
 67 
     | 
    
         
            +
                expect(
         
     | 
| 
      
 68 
     | 
    
         
            +
                  Either.attempt_all do
         
     | 
| 
      
 69 
     | 
    
         
            +
                    try { 1 }
         
     | 
| 
      
 70 
     | 
    
         
            +
                    let { |v| Success(v + 1) }
         
     | 
| 
      
 71 
     | 
    
         
            +
                  end
         
     | 
| 
      
 72 
     | 
    
         
            +
                ).to eq Success(2)
         
     | 
| 
      
 73 
     | 
    
         
            +
              end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
       67 
75 
     | 
    
         
             
              it "works with an OpenStruct" do
         
     | 
| 
       68 
76 
     | 
    
         
             
                context = OpenStruct.new
         
     | 
| 
       69 
77 
     | 
    
         
             
                attempt = Either.attempt_all(context) do
         
     | 
| 
         @@ -75,23 +83,23 @@ describe Deterministic::Either::AttemptAll do 
     | 
|
| 
       75 
83 
     | 
    
         
             
                expect(context.alpha).to eq 2
         
     | 
| 
       76 
84 
     | 
    
         
             
              end
         
     | 
| 
       77 
85 
     | 
    
         | 
| 
       78 
     | 
    
         
            -
              it "can operate in the context of a  
     | 
| 
       79 
     | 
    
         
            -
                class  
     | 
| 
      
 86 
     | 
    
         
            +
              it "can operate in the context of a context" do
         
     | 
| 
      
 87 
     | 
    
         
            +
                class Context
         
     | 
| 
       80 
88 
     | 
    
         
             
                  attr_accessor :a
         
     | 
| 
       81 
89 
     | 
    
         
             
                  def initialize
         
     | 
| 
       82 
90 
     | 
    
         
             
                    @a = 1
         
     | 
| 
       83 
91 
     | 
    
         
             
                  end
         
     | 
| 
       84 
92 
     | 
    
         
             
                end
         
     | 
| 
       85 
93 
     | 
    
         | 
| 
       86 
     | 
    
         
            -
                 
     | 
| 
      
 94 
     | 
    
         
            +
                context = Context.new
         
     | 
| 
       87 
95 
     | 
    
         | 
| 
       88 
96 
     | 
    
         
             
                expect(
         
     | 
| 
       89 
     | 
    
         
            -
                  Either.attempt_all( 
     | 
| 
      
 97 
     | 
    
         
            +
                  Either.attempt_all(context) do
         
     | 
| 
       90 
98 
     | 
    
         
             
                    try { self.a += 1 }
         
     | 
| 
       91 
99 
     | 
    
         
             
                    try { self.a + 1 }
         
     | 
| 
       92 
100 
     | 
    
         
             
                  end
         
     | 
| 
       93 
101 
     | 
    
         
             
                ).to eq Success(3)
         
     | 
| 
       94 
102 
     | 
    
         | 
| 
       95 
     | 
    
         
            -
                expect( 
     | 
| 
      
 103 
     | 
    
         
            +
                expect(context.a).to eq 2
         
     | 
| 
       96 
104 
     | 
    
         
             
              end
         
     | 
| 
       97 
105 
     | 
    
         
             
            end
         
     | 
| 
         @@ -1,5 +1,4 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            require 'spec_helper'
         
     | 
| 
       2 
     | 
    
         
            -
            require 'deterministic'
         
     | 
| 
       3 
2 
     | 
    
         | 
| 
       4 
3 
     | 
    
         
             
            include Deterministic
         
     | 
| 
       5 
4 
     | 
    
         | 
| 
         @@ -34,6 +33,20 @@ describe Deterministic::Either::Match do 
     | 
|
| 
       34 
33 
     | 
    
         
             
                ).to eq "matched 2"
         
     | 
| 
       35 
34 
     | 
    
         
             
              end
         
     | 
| 
       36 
35 
     | 
    
         | 
| 
      
 36 
     | 
    
         
            +
              it "can match with classes" do
         
     | 
| 
      
 37 
     | 
    
         
            +
                expect(
         
     | 
| 
      
 38 
     | 
    
         
            +
                  Success([1, 2, 3]).match do
         
     | 
| 
      
 39 
     | 
    
         
            +
                    success(Array) { |v| v.first }
         
     | 
| 
      
 40 
     | 
    
         
            +
                  end
         
     | 
| 
      
 41 
     | 
    
         
            +
                ).to eq 1
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                expect(
         
     | 
| 
      
 44 
     | 
    
         
            +
                  Success(1).match do
         
     | 
| 
      
 45 
     | 
    
         
            +
                    success(Fixnum) { |v| v }
         
     | 
| 
      
 46 
     | 
    
         
            +
                  end
         
     | 
| 
      
 47 
     | 
    
         
            +
                ).to eq 1
         
     | 
| 
      
 48 
     | 
    
         
            +
              end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
       37 
50 
     | 
    
         
             
              it "catch-all" do
         
     | 
| 
       38 
51 
     | 
    
         
             
                expect(
         
     | 
| 
       39 
52 
     | 
    
         
             
                  Success(1).match do
         
     | 
| 
         @@ -55,6 +68,7 @@ describe Deterministic::Either::Match do 
     | 
|
| 
       55 
68 
     | 
    
         
             
              it "can match with lambdas" do
         
     | 
| 
       56 
69 
     | 
    
         
             
                expect(
         
     | 
| 
       57 
70 
     | 
    
         
             
                  Success(1).match do
         
     | 
| 
      
 71 
     | 
    
         
            +
                    failure                  { "not me" }
         
     | 
| 
       58 
72 
     | 
    
         
             
                    success ->(v) { v == 1 } { |v| "matched #{v}" }
         
     | 
| 
       59 
73 
     | 
    
         
             
                  end
         
     | 
| 
       60 
74 
     | 
    
         
             
                ).to eq "matched 1"
         
     | 
| 
         @@ -1,6 +1,5 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            require 'spec_helper'
         
     | 
| 
       2 
2 
     | 
    
         
             
            require_relative '../monad_axioms'
         
     | 
| 
       3 
     | 
    
         
            -
            require 'deterministic'
         
     | 
| 
       4 
3 
     | 
    
         | 
| 
       5 
4 
     | 
    
         
             
            include Deterministic
         
     | 
| 
       6 
5 
     | 
    
         | 
| 
         @@ -17,7 +16,6 @@ describe Deterministic::Success do 
     | 
|
| 
       17 
16 
     | 
    
         
             
              specify { expect(subject).to be_success }
         
     | 
| 
       18 
17 
     | 
    
         
             
              specify { expect(subject).not_to be_failure }
         
     | 
| 
       19 
18 
     | 
    
         | 
| 
       20 
     | 
    
         
            -
              # public constructor #Success[]
         
     | 
| 
       21 
19 
     | 
    
         
             
              specify { expect(subject).to be_an_instance_of described_class }
         
     | 
| 
       22 
20 
     | 
    
         
             
              specify { expect(subject).to eq(described_class.new(1)) }
         
     | 
| 
       23 
21 
     | 
    
         
             
              specify { expect(subject << Success(2)).to eq(Success(2)) }
         
     | 
| 
         @@ -30,6 +28,4 @@ describe Deterministic::Success do 
     | 
|
| 
       30 
28 
     | 
    
         
             
                  subject.bind { |v| true ? Success(v + 1) : Failure(v + 2)}
         
     | 
| 
       31 
29 
     | 
    
         
             
                ).to eq Success(2)
         
     | 
| 
       32 
30 
     | 
    
         
             
              end
         
     | 
| 
       33 
     | 
    
         
            -
             
     | 
| 
       34 
     | 
    
         
            -
              specify { expect { Success("a").bind(&:upcase) }.to raise_error(Deterministic::Monad::NotMonadError) }
         
     | 
| 
       35 
31 
     | 
    
         
             
            end
         
     | 
| 
         @@ -1,6 +1,5 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            require 'spec_helper'
         
     | 
| 
       2 
2 
     | 
    
         
             
            require_relative 'monad_axioms'
         
     | 
| 
       3 
     | 
    
         
            -
            require 'deterministic'
         
     | 
| 
       4 
3 
     | 
    
         | 
| 
       5 
4 
     | 
    
         | 
| 
       6 
5 
     | 
    
         
             
            describe Deterministic::Monad do
         
     | 
| 
         @@ -17,7 +16,28 @@ describe Deterministic::Monad do 
     | 
|
| 
       17 
16 
     | 
    
         
             
              specify { expect(Identity.new([1, 2]).map(&:to_s)).to eq Identity.new("[1, 2]") }
         
     | 
| 
       18 
17 
     | 
    
         
             
              specify { expect(Identity.new(1).map {|v| v + 2}).to eq Identity.new(3) }
         
     | 
| 
       19 
18 
     | 
    
         
             
              specify { expect(Identity.new('foo').map(&:upcase)).to eq Identity.new('FOO')}
         
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
              context '#bind' do
         
     | 
| 
      
 21 
     | 
    
         
            +
                it "raises an error if the passed function does not return a monad of the same class" do 
         
     | 
| 
      
 22 
     | 
    
         
            +
                  expect { Identity.new(1).bind {} }.to raise_error(Deterministic::Monad::NotMonadError)
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
                specify { expect(Identity.new(1).bind {|value| Identity.new(value) }).to eq Identity.new(1) }
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                it "passes the monad class, this is ruby-fu?!" do
         
     | 
| 
      
 27 
     | 
    
         
            +
                 Identity.new(1)
         
     | 
| 
      
 28 
     | 
    
         
            +
                  .bind do |_, monad|
         
     | 
| 
      
 29 
     | 
    
         
            +
                    p self.class
         
     | 
| 
      
 30 
     | 
    
         
            +
                    expect(monad).to eq Identity
         
     | 
| 
      
 31 
     | 
    
         
            +
                    monad.new(_)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  end
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                specify { expect(
         
     | 
| 
      
 36 
     | 
    
         
            +
                  Identity.new(1).bind { |value, monad| monad.new(value + 1) }
         
     | 
| 
      
 37 
     | 
    
         
            +
                  ).to eq Identity.new(2)
         
     | 
| 
      
 38 
     | 
    
         
            +
                }
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
              end
         
     | 
| 
       21 
41 
     | 
    
         
             
              specify { expect(Identity.new(Identity.new(1))).to eq Identity.new(1) }
         
     | 
| 
       22 
42 
     | 
    
         | 
| 
       23 
43 
     | 
    
         
             
              # it 'delegates #flat_map to an underlying collection and wraps the resulting collection' do
         
     | 
    
        data/spec/spec_helper.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | 
         @@ -1,14 +1,14 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: deterministic
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 0.1. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.1.2
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Piotr Zolnierek
         
     | 
| 
       8 
8 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       9 
9 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       10 
10 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       11 
     | 
    
         
            -
            date:  
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2014-01-06 00:00:00.000000000 Z
         
     | 
| 
       12 
12 
     | 
    
         
             
            dependencies:
         
     | 
| 
       13 
13 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       14 
14 
     | 
    
         
             
              name: bundler
         
     | 
| 
         @@ -132,9 +132,12 @@ files: 
     | 
|
| 
       132 
132 
     | 
    
         
             
            - lib/deterministic/either/success.rb
         
     | 
| 
       133 
133 
     | 
    
         
             
            - lib/deterministic/monad.rb
         
     | 
| 
       134 
134 
     | 
    
         
             
            - lib/deterministic/version.rb
         
     | 
| 
      
 135 
     | 
    
         
            +
            - spec/examples/bookings_spec.rb
         
     | 
| 
      
 136 
     | 
    
         
            +
            - spec/examples/validate_address_spec.rb
         
     | 
| 
       135 
137 
     | 
    
         
             
            - spec/lib/deterministic/attempt_all_spec.rb
         
     | 
| 
       136 
138 
     | 
    
         
             
            - spec/lib/deterministic/either/match_spec.rb
         
     | 
| 
       137 
139 
     | 
    
         
             
            - spec/lib/deterministic/either/success_spec.rb
         
     | 
| 
      
 140 
     | 
    
         
            +
            - spec/lib/deterministic/either_spec.rb
         
     | 
| 
       138 
141 
     | 
    
         
             
            - spec/lib/deterministic/monad_axioms.rb
         
     | 
| 
       139 
142 
     | 
    
         
             
            - spec/lib/deterministic/monad_spec.rb
         
     | 
| 
       140 
143 
     | 
    
         
             
            - spec/spec_helper.rb
         
     | 
| 
         @@ -158,14 +161,17 @@ required_rubygems_version: !ruby/object:Gem::Requirement 
     | 
|
| 
       158 
161 
     | 
    
         
             
                  version: '0'
         
     | 
| 
       159 
162 
     | 
    
         
             
            requirements: []
         
     | 
| 
       160 
163 
     | 
    
         
             
            rubyforge_project: 
         
     | 
| 
       161 
     | 
    
         
            -
            rubygems_version: 2.0. 
     | 
| 
      
 164 
     | 
    
         
            +
            rubygems_version: 2.0.3
         
     | 
| 
       162 
165 
     | 
    
         
             
            signing_key: 
         
     | 
| 
       163 
166 
     | 
    
         
             
            specification_version: 4
         
     | 
| 
       164 
167 
     | 
    
         
             
            summary: see above
         
     | 
| 
       165 
168 
     | 
    
         
             
            test_files:
         
     | 
| 
      
 169 
     | 
    
         
            +
            - spec/examples/bookings_spec.rb
         
     | 
| 
      
 170 
     | 
    
         
            +
            - spec/examples/validate_address_spec.rb
         
     | 
| 
       166 
171 
     | 
    
         
             
            - spec/lib/deterministic/attempt_all_spec.rb
         
     | 
| 
       167 
172 
     | 
    
         
             
            - spec/lib/deterministic/either/match_spec.rb
         
     | 
| 
       168 
173 
     | 
    
         
             
            - spec/lib/deterministic/either/success_spec.rb
         
     | 
| 
      
 174 
     | 
    
         
            +
            - spec/lib/deterministic/either_spec.rb
         
     | 
| 
       169 
175 
     | 
    
         
             
            - spec/lib/deterministic/monad_axioms.rb
         
     | 
| 
       170 
176 
     | 
    
         
             
            - spec/lib/deterministic/monad_spec.rb
         
     | 
| 
       171 
177 
     | 
    
         
             
            - spec/spec_helper.rb
         
     |