deterministic 0.12.1 → 0.13.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/HISTORY.md +18 -0
- data/README.md +92 -35
- data/lib/deterministic/either.rb +35 -0
- data/lib/deterministic/match.rb +3 -2
- data/lib/deterministic/monad.rb +1 -0
- data/lib/deterministic/option.rb +28 -11
- data/lib/deterministic/result.rb +39 -4
- data/lib/deterministic/version.rb +1 -1
- data/lib/deterministic.rb +1 -1
- data/spec/lib/deterministic/either_spec.rb +29 -0
- data/spec/lib/deterministic/option_spec.rb +54 -25
- metadata +7 -5
- data/lib/deterministic/result/chain.rb +0 -27
- /data/spec/lib/deterministic/result/{chain_spec.rb → result_map.rb} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 120b61f2caf3653ca63fc4bca59908c8c3add91b
|
4
|
+
data.tar.gz: 2813dd0b90f301a5afcbc7861e446c3bdc507f15
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d3195c0fbd1f114843e7003e84b9e24ba129933986ef948f44f19be83202719148b44554d27146acd0addd728aab98bb265356ea1d14e8338b8eaee705bdd91e
|
7
|
+
data.tar.gz: 721194e06a0618864efffcf079f6cc4220c58b3e0a962e7f2c43186d01e2bcc8825d34999c7c8a4abe8270bd6153901d30dd46393f7e8327a14bdb640a95f094
|
data/HISTORY.md
CHANGED
@@ -1,3 +1,21 @@
|
|
1
|
+
## v0.13.0
|
2
|
+
|
3
|
+
- Add `Option#value_to_a`
|
4
|
+
- Add `Option#+`
|
5
|
+
- Make `None` a real monad (little impact on the real world)
|
6
|
+
- Add `Either`
|
7
|
+
- Add `Option#value_or`
|
8
|
+
- `None#value` is now private
|
9
|
+
|
10
|
+
## v0.12.1
|
11
|
+
|
12
|
+
- Fix backwards compatibility with Ruby < 2.0.0
|
13
|
+
|
14
|
+
## v0.12.0
|
15
|
+
|
16
|
+
- Add Option
|
17
|
+
- Nest `Success` and `Failure` under `Result`
|
18
|
+
|
1
19
|
## v0.10.0
|
2
20
|
** breaking changes **
|
3
21
|
|
data/README.md
CHANGED
@@ -6,6 +6,29 @@ Deterministic is to help your code to be more confident, it's specialty is flow
|
|
6
6
|
|
7
7
|
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.
|
8
8
|
|
9
|
+
## Patterns
|
10
|
+
|
11
|
+
Deterministic provides different monads, here is a short guide, when to use which
|
12
|
+
|
13
|
+
#### Result: Success & Failure
|
14
|
+
- an operation which can succeed or fail
|
15
|
+
- the result (content) of of the success or failure is important
|
16
|
+
- you are building one thing
|
17
|
+
- chaining: if one fails (Failure), don't execute the rest
|
18
|
+
|
19
|
+
#### Option: Some & None
|
20
|
+
- an operation which returns either some result or nothing
|
21
|
+
- in case it returns nothing it is not important to know why
|
22
|
+
- you are working rather with a collection of things
|
23
|
+
- chaining: execute all and then select the successful ones (Some)
|
24
|
+
|
25
|
+
#### Either: Left & Right
|
26
|
+
- an operation which returns several good and bad results
|
27
|
+
- the results of both are important
|
28
|
+
- chaining: if one fails, continue, the content of the failed and successful are important
|
29
|
+
|
30
|
+
#### Maybe
|
31
|
+
- an object may be nil, you want to avoid endless nil? checks
|
9
32
|
|
10
33
|
## Usage
|
11
34
|
|
@@ -19,8 +42,6 @@ Failure(1).to_s # => "1"
|
|
19
42
|
Failure(Failure(1)) # => Failure(1)
|
20
43
|
```
|
21
44
|
|
22
|
-
#### `fmap(self: Result(a), op: |a| -> b) -> Result(b)`
|
23
|
-
|
24
45
|
Maps a `Result` with the value `a` to the same `Result` with the value `b`.
|
25
46
|
|
26
47
|
```ruby
|
@@ -28,82 +49,62 @@ Success(1).fmap { |v| v + 1} # => Success(2)
|
|
28
49
|
Failure(1).fmap { |v| v - 1} # => Failure(0)
|
29
50
|
```
|
30
51
|
|
31
|
-
#### `bind(self: Result(a), op: |a| -> Result(b)) -> Result(b)`
|
32
|
-
|
33
52
|
Maps a `Result` with the value `a` to another `Result` with the value `b`.
|
34
53
|
|
35
54
|
```ruby
|
36
55
|
Success(1).bind { |v| Failure(v + 1) } # => Failure(2)
|
37
|
-
Failure(1).
|
56
|
+
Failure(1).bind { |v| Success(v - 1) } # => Success(0)
|
38
57
|
```
|
39
58
|
|
40
|
-
#### `map(self: Success(a), op: |a| -> Result(b)) -> Result(b)`
|
41
|
-
|
42
59
|
Maps a `Success` with the value `a` to another `Result` with the value `b`. It works like `#bind` but only on `Success`.
|
43
60
|
|
44
61
|
```ruby
|
45
62
|
Success(1).map { |n| n + 1 } # => Success(2)
|
46
63
|
Failure(0).map { |n| n + 1 } # => Failure(0)
|
47
64
|
```
|
48
|
-
|
49
|
-
#### `map_err(self: Failure(a), op: |a| -> Result(b)) -> Result(b)`
|
50
|
-
|
51
65
|
Maps a `Failure` with the value `a` to another `Result` with the value `b`. It works like `#bind` but only on `Failure`.
|
52
66
|
|
53
67
|
```ruby
|
54
|
-
Failure(1).map_err { |n| n + 1 }
|
55
|
-
Success(0).map_err { |n| n + 1 }
|
68
|
+
Failure(1).map_err { |n| n + 1 } # => Success(2)
|
69
|
+
Success(0).map_err { |n| n + 1 } # => Success(0)
|
56
70
|
```
|
57
71
|
|
58
|
-
#### `try(self: Success(a), op: |a| -> Result(b)) -> Result(b)`
|
59
|
-
|
60
|
-
Just like `#map`, transforms `a` to another `Result`, but it will also catch raised exceptions and wrap them with a `Failure`.
|
61
|
-
|
62
72
|
```ruby
|
63
73
|
Success(0).try { |n| raise "Error" } # => Failure(Error)
|
64
74
|
```
|
65
75
|
|
66
|
-
#### `and(self: Success(a), other: Result(b)) -> Result(b)`
|
67
|
-
|
68
76
|
Replaces `Success a` with `Result b`. If a `Failure` is passed as argument, it is ignored.
|
69
77
|
|
70
78
|
```ruby
|
71
|
-
Success(1).and Success(2)
|
72
|
-
Failure(1).and Success(2)
|
79
|
+
Success(1).and Success(2) # => Success(2)
|
80
|
+
Failure(1).and Success(2) # => Failure(1)
|
73
81
|
```
|
74
82
|
|
75
|
-
#### `and_then(self: Success(a), op: |a| -> Result(b)) -> Result(b)`
|
76
|
-
|
77
83
|
Replaces `Success a` with the result of the block. If a `Failure` is passed as argument, it is ignored.
|
78
84
|
|
79
85
|
```ruby
|
80
|
-
Success(1).and_then { Success(2) }
|
81
|
-
Failure(1).and_then { Success(2) }
|
86
|
+
Success(1).and_then { Success(2) } # => Success(2)
|
87
|
+
Failure(1).and_then { Success(2) } # => Failure(1)
|
82
88
|
```
|
83
89
|
|
84
|
-
#### `or(self: Failure(a), other: Result(b)) -> Result(b)`
|
85
90
|
Replaces `Failure a` with `Result`. If a `Failure` is passed as argument, it is ignored.
|
86
91
|
|
87
92
|
```ruby
|
88
|
-
Success(1).or Success(2)
|
89
|
-
Failure(1).or Success(1)
|
93
|
+
Success(1).or Success(2) # => Success(1)
|
94
|
+
Failure(1).or Success(1) # => Success(1)
|
90
95
|
```
|
91
96
|
|
92
|
-
#### `or_else(self: Failure(a), op: |a| -> Result(b)) -> Result(b)`
|
93
|
-
|
94
97
|
Replaces `Failure a` with the result of the block. If a `Success` is passed as argument, it is ignored.
|
95
98
|
|
96
99
|
```ruby
|
97
|
-
Success(1).or_else { Success(2) }
|
98
|
-
Failure(1).or_else { |n| Success(n)}
|
100
|
+
Success(1).or_else { Success(2) } # => Success(1)
|
101
|
+
Failure(1).or_else { |n| Success(n)} # => Success(1)
|
99
102
|
```
|
100
103
|
|
101
|
-
#### `pipe(self: Result(a), op: |Result(a)| -> b) -> Result(a)`
|
102
|
-
|
103
104
|
Executes the block passed, but completely ignores its result. If an error is raised within the block it will **NOT** be catched.
|
104
105
|
|
105
106
|
```ruby
|
106
|
-
Success(1).try { |n| log(n.value) }
|
107
|
+
Success(1).try { |n| log(n.value) } # => Success(1)
|
107
108
|
```
|
108
109
|
|
109
110
|
The value or block result must always be a `Result` i.e. `Success` or `Failure`.
|
@@ -256,7 +257,7 @@ end # => "catch-all"
|
|
256
257
|
## core_ext
|
257
258
|
You can use a core extension, to include Result in your own class or in Object, i.e. in all classes.
|
258
259
|
|
259
|
-
|
260
|
+
|
260
261
|
|
261
262
|
```ruby
|
262
263
|
require 'deterministic/core_ext/object/result'
|
@@ -267,6 +268,62 @@ Success(1).success? # => true
|
|
267
268
|
Failure(1).result? # => true
|
268
269
|
```
|
269
270
|
|
271
|
+
## Option
|
272
|
+
|
273
|
+
```ruby
|
274
|
+
Some(1).some? # #=> true
|
275
|
+
Some(1).none? # #=> false
|
276
|
+
None.some? # #=> false
|
277
|
+
None.none? # #=> true
|
278
|
+
|
279
|
+
Some(1).fmap { |n| n + 1 } # => Some(2)
|
280
|
+
None.fmap { |n| n + 1 } # => None
|
281
|
+
|
282
|
+
Some(1).map { |n| Some(n + 1) } # => Some(2)
|
283
|
+
Some(1).map { |n| None } # => None
|
284
|
+
None.map { |n| Some(n + 1) } # => None
|
285
|
+
|
286
|
+
Some(1).value # => 1
|
287
|
+
Some(1).value_or(2) # => 1
|
288
|
+
None.value # => NoMethodError
|
289
|
+
None.value_or(0) # => 0
|
290
|
+
|
291
|
+
Some(1).value_to_a # => Some([1])
|
292
|
+
Some([1]).value_to_a # => Some([1])
|
293
|
+
None.value_to_a # => None
|
294
|
+
|
295
|
+
Some(1) + Some(1) # => Some(2)
|
296
|
+
Some([1]) + Some(1) # => TypeError: No implicit conversion
|
297
|
+
None + Some(1) # => Some(1)
|
298
|
+
Some(1) + None # => Some(1)
|
299
|
+
Some([1]) + None + Some([2]) # => Some([1, 2])
|
300
|
+
```
|
301
|
+
|
302
|
+
### Coercion
|
303
|
+
```ruby
|
304
|
+
Option.any?(nil) # => None
|
305
|
+
Option.any?([]) # => None
|
306
|
+
Option.any?({}) # => None
|
307
|
+
Option.any?(1) # => Some(1)
|
308
|
+
|
309
|
+
Option.some?(nil) # => None
|
310
|
+
Option.some?([]) # => Some([])
|
311
|
+
Option.some?({}) # => Some({})
|
312
|
+
Option.some?(1) # => Some(1)
|
313
|
+
|
314
|
+
Option.try! { 1 } # => Some(1)
|
315
|
+
Option.try! { raise "error"} # => None
|
316
|
+
```
|
317
|
+
|
318
|
+
### Pattern Matching
|
319
|
+
```ruby
|
320
|
+
Some(1).match {
|
321
|
+
some(1) { |n| n + 1 }
|
322
|
+
some { 1 }
|
323
|
+
none { 0 }
|
324
|
+
} # => 2
|
325
|
+
```
|
326
|
+
|
270
327
|
|
271
328
|
## Maybe
|
272
329
|
The simplest NullObject wrapper there can be. It adds `#some?` and `#null?` to `Object` though.
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Deterministic
|
2
|
+
class Either
|
3
|
+
include Monad
|
4
|
+
class << self
|
5
|
+
public :new
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(left=[], right=[])
|
9
|
+
@left, @right = left, right
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :left, :right
|
13
|
+
|
14
|
+
def +(other)
|
15
|
+
raise Deterministic::Monad::NotMonadError, "Expected an Either, got #{other.class}" unless other.is_a? Either
|
16
|
+
|
17
|
+
Either.new(left + other.left, right + other.right)
|
18
|
+
end
|
19
|
+
|
20
|
+
undef :value
|
21
|
+
|
22
|
+
def inspect
|
23
|
+
"Either(left: #{left.inspect}, right: #{right.inspect})"
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
module_function
|
28
|
+
def Left(value)
|
29
|
+
Either.new(Array[value], [])
|
30
|
+
end
|
31
|
+
|
32
|
+
def Right(value)
|
33
|
+
Either.new([], Array[value])
|
34
|
+
end
|
35
|
+
end
|
data/lib/deterministic/match.rb
CHANGED
@@ -18,9 +18,10 @@ module Deterministic
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def call
|
21
|
-
|
21
|
+
value = @container.respond_to?(:value) ? @container.value : nil
|
22
|
+
matcher = @collection.detect { |m| m.matches?(value) }
|
22
23
|
raise NoMatchError, "No match could be made for #{@container.inspect}" if matcher.nil?
|
23
|
-
@context.instance_exec(
|
24
|
+
@context.instance_exec(value, &matcher.block)
|
24
25
|
end
|
25
26
|
|
26
27
|
# catch-all
|
data/lib/deterministic/monad.rb
CHANGED
data/lib/deterministic/option.rb
CHANGED
@@ -45,6 +45,22 @@ module Deterministic
|
|
45
45
|
bind(proc || block)
|
46
46
|
end
|
47
47
|
|
48
|
+
## Convert the inner value to an Array
|
49
|
+
def value_to_a
|
50
|
+
map { self.class.new(Array(value)) }
|
51
|
+
end
|
52
|
+
|
53
|
+
## Add the inner values of two Some
|
54
|
+
def +(other)
|
55
|
+
return other if none?
|
56
|
+
fmap { |v|
|
57
|
+
other.match {
|
58
|
+
some { v + other.value }
|
59
|
+
none { self }
|
60
|
+
}
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
48
64
|
def some?
|
49
65
|
is_a? Some
|
50
66
|
end
|
@@ -53,35 +69,36 @@ module Deterministic
|
|
53
69
|
is_a? None
|
54
70
|
end
|
55
71
|
|
72
|
+
def value_or(default)
|
73
|
+
return default if none?
|
74
|
+
return value
|
75
|
+
end
|
76
|
+
|
56
77
|
class Some < Option
|
57
78
|
class << self; public :new; end
|
58
79
|
end
|
59
80
|
|
60
81
|
class None < Option
|
61
82
|
class << self; public :new; end
|
62
|
-
def initialize(*args)
|
83
|
+
def initialize(*args)
|
84
|
+
@value = self
|
85
|
+
end
|
63
86
|
|
64
87
|
def inspect
|
65
88
|
"None"
|
66
89
|
end
|
67
90
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
def none(*args)
|
91
|
+
private :value
|
92
|
+
|
93
|
+
def fmap(*args)
|
72
94
|
self
|
73
95
|
end
|
74
96
|
|
75
|
-
alias :
|
76
|
-
alias :map :none
|
97
|
+
alias :map :fmap
|
77
98
|
|
78
99
|
def ==(other)
|
79
100
|
other.class == self.class
|
80
101
|
end
|
81
|
-
|
82
|
-
# def value
|
83
|
-
# self # raise "value called on a None"
|
84
|
-
# end
|
85
102
|
end
|
86
103
|
end
|
87
104
|
|
data/lib/deterministic/result.rb
CHANGED
@@ -18,7 +18,11 @@ module Deterministic
|
|
18
18
|
end
|
19
19
|
|
20
20
|
include PatternMatching
|
21
|
-
|
21
|
+
|
22
|
+
# This is an abstract class, can't ever instantiate it directly
|
23
|
+
class << self
|
24
|
+
protected :new
|
25
|
+
end
|
22
26
|
|
23
27
|
def success?
|
24
28
|
is_a? Success
|
@@ -28,6 +32,8 @@ module Deterministic
|
|
28
32
|
is_a? Failure
|
29
33
|
end
|
30
34
|
|
35
|
+
# `pipe(self: Result(a), op: |Result(a)| -> b) -> Result(a)`
|
36
|
+
# Executes the block passed, but completely ignores its result. If an error is raised within the block it will **NOT** be catched.
|
31
37
|
def pipe(proc=nil, &block)
|
32
38
|
(proc || block).call(self)
|
33
39
|
self
|
@@ -35,32 +41,61 @@ module Deterministic
|
|
35
41
|
|
36
42
|
alias :** :pipe
|
37
43
|
|
44
|
+
# `pipe(self: Result(a), op: |Result(a)| -> b) -> Result(a)`
|
45
|
+
# Replaces `Success a` with `Result b`. If a `Failure` is passed as argument, it is ignored.
|
38
46
|
def and(other)
|
39
47
|
return self if failure?
|
40
48
|
raise NotMonadError, "Expected #{other.inspect} to be an Result" unless other.is_a? Result
|
41
49
|
other
|
42
50
|
end
|
43
51
|
|
52
|
+
# `and_then(self: Success(a), op: |a| -> Result(b)) -> Result(b)`
|
53
|
+
# Replaces `Success a` with the result of the block. If a `Failure` is passed as argument, it is ignored.
|
44
54
|
def and_then(&block)
|
45
55
|
return self if failure?
|
46
56
|
bind(&block)
|
47
57
|
end
|
48
58
|
|
59
|
+
# `or(self: Failure(a), other: Result(b)) -> Result(b)`
|
60
|
+
# Replaces `Failure a` with `Result`. If a `Failure` is passed as argument, it is ignored.
|
49
61
|
def or(other)
|
50
62
|
return self if success?
|
51
63
|
raise NotMonadError, "Expected #{other.inspect} to be an Result" unless other.is_a? Result
|
52
64
|
return other
|
53
65
|
end
|
54
66
|
|
67
|
+
# `or_else(self: Failure(a), op: |a| -> Result(b)) -> Result(b)`
|
68
|
+
# Replaces `Failure a` with the result of the block. If a `Success` is passed as argument, it is ignored.
|
55
69
|
def or_else(&block)
|
56
70
|
return self if success?
|
57
71
|
bind(&block)
|
58
72
|
end
|
59
73
|
|
60
|
-
#
|
61
|
-
|
62
|
-
|
74
|
+
# `map(self: Success(a), op: |a| -> Result(b)) -> Result(b)`
|
75
|
+
# Maps a `Success` with the value `a` to another `Result` with the value `b`. It works like `#bind` but only on `Success`.
|
76
|
+
def map(proc=nil, &block)
|
77
|
+
return self if failure?
|
78
|
+
bind(proc || block)
|
79
|
+
end
|
80
|
+
|
81
|
+
alias :>> :map
|
82
|
+
|
83
|
+
# `map_err(self: Failure(a), op: |a| -> Result(b)) -> Result(b)`
|
84
|
+
# Maps a `Failure` with the value `a` to another `Result` with the value `b`. It works like `#bind` but only on `Failure`.
|
85
|
+
def map_err(proc=nil, &block)
|
86
|
+
return self if success?
|
87
|
+
bind(proc || block)
|
88
|
+
end
|
89
|
+
|
90
|
+
# `pipe(self: Result(a), op: |Result(a)| -> b) -> Result(a)`
|
91
|
+
# Executes the block passed, but completely ignores its result. If an error is raised within the block it will **NOT** be catched.
|
92
|
+
def try(proc=nil, &block)
|
93
|
+
map(proc, &block)
|
94
|
+
rescue => err
|
95
|
+
Failure(err)
|
63
96
|
end
|
97
|
+
|
98
|
+
alias :>= :try
|
64
99
|
|
65
100
|
class Failure < Result
|
66
101
|
class << self; public :new; end
|
data/lib/deterministic.rb
CHANGED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
include Deterministic
|
4
|
+
|
5
|
+
describe Deterministic::Either do
|
6
|
+
it "+ does not change operands" do
|
7
|
+
l = Left(1)
|
8
|
+
r = Right(2)
|
9
|
+
|
10
|
+
either = l + r
|
11
|
+
expect(l).to eq Left(1)
|
12
|
+
expect(r).to eq Right(2)
|
13
|
+
expect(either).to eq Either.new([1], [2])
|
14
|
+
end
|
15
|
+
|
16
|
+
it "allows adding multiple Eithers" do
|
17
|
+
either = Left(1) + Left(2) + Right(:a) + Right(:b)
|
18
|
+
expect(either.left).to eq [1, 2]
|
19
|
+
expect(either.right).to eq [:a, :b]
|
20
|
+
end
|
21
|
+
|
22
|
+
it "works" do
|
23
|
+
actual = [1, 2, 3, 4].inject(Either.new) { |acc, value|
|
24
|
+
acc + (value % 2 == 0 ? Right(value) : Left(value))
|
25
|
+
}
|
26
|
+
expect(actual).to eq Either.new([1, 3], [2, 4])
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -11,6 +11,7 @@ describe Deterministic::Option do
|
|
11
11
|
|
12
12
|
# any?
|
13
13
|
specify { expect(described_class.any?(nil)).to be_none }
|
14
|
+
specify { expect(described_class.any?(None)).to be_none }
|
14
15
|
specify { expect(described_class.any?("")).to be_none }
|
15
16
|
specify { expect(described_class.any?([])).to be_none }
|
16
17
|
specify { expect(described_class.any?({})).to be_none }
|
@@ -21,21 +22,53 @@ describe Deterministic::Option do
|
|
21
22
|
specify { expect(described_class.try! { raise "error" }).to be_none }
|
22
23
|
end
|
23
24
|
|
24
|
-
describe Deterministic::Option
|
25
|
-
|
26
|
-
|
27
|
-
end
|
25
|
+
describe Deterministic::Option do
|
26
|
+
# it_behaves_like 'a Monad' do
|
27
|
+
# let(:monad) { described_class }
|
28
|
+
# end
|
29
|
+
|
30
|
+
specify { expect(Option::Some.new(0)).to be_a Option::Some }
|
31
|
+
specify { expect(Option::Some.new(0)).to eq Some(0) }
|
32
|
+
|
33
|
+
specify { expect(Option::None.new).to eq Option::None.new }
|
34
|
+
specify { expect(Option::None.new).to eq None }
|
35
|
+
|
36
|
+
# some?, none?
|
37
|
+
specify { expect(None.some?).to be_falsey }
|
38
|
+
specify { expect(None.none?).to be_truthy }
|
39
|
+
specify { expect(Some(0).some?).to be_truthy }
|
40
|
+
specify { expect(Some(0).none?).to be_falsey }
|
41
|
+
|
42
|
+
# value, value_or
|
43
|
+
specify { expect(Some(0).value).to eq 0 }
|
44
|
+
specify { expect(Some(1).value_or(2)).to eq 1}
|
45
|
+
specify { expect { None.value }.to raise_error NoMethodError }
|
46
|
+
specify { expect(None.value_or(2)).to eq 2}
|
47
|
+
|
48
|
+
# fmap
|
49
|
+
specify { expect(Some(1).fmap { |n| n + 1}).to eq Some(2) }
|
50
|
+
specify { expect(None.fmap { |n| n + 1}).to eq None }
|
51
|
+
|
52
|
+
# map
|
53
|
+
specify { expect(Some(1).map { |n| Some(n + 1)}).to eq Some(2) }
|
54
|
+
specify { expect(Some(1).map { |n| None }).to eq None }
|
55
|
+
specify { expect(None.map { |n| nil }).to eq None }
|
28
56
|
|
29
|
-
|
30
|
-
specify { expect(
|
31
|
-
specify { expect(
|
32
|
-
specify { expect(
|
33
|
-
specify { expect(described_class.new(0).value).to eq 0 }
|
57
|
+
# to_a
|
58
|
+
specify { expect(Some(1).value_to_a). to eq Some([1])}
|
59
|
+
specify { expect(Some([1]).value_to_a). to eq Some([1])}
|
60
|
+
specify { expect(None.value_to_a). to eq None}
|
34
61
|
|
35
|
-
|
36
|
-
specify { expect(
|
37
|
-
specify { expect(
|
62
|
+
# +
|
63
|
+
specify { expect(Some(1) + None).to eq Some(1) }
|
64
|
+
specify { expect(Some(1) + None + None).to eq Some(1) }
|
65
|
+
specify { expect(Some(1) + Some(1)).to eq Some(2) }
|
66
|
+
specify { expect(None + Some(1)).to eq Some(1) }
|
67
|
+
specify { expect(None + None + Some(1)).to eq Some(1) }
|
68
|
+
specify { expect(None + None + Some(1) + None).to eq Some(1) }
|
69
|
+
specify { expect { Some([1]) + Some(1)}.to raise_error TypeError}
|
38
70
|
|
71
|
+
# match
|
39
72
|
specify {
|
40
73
|
expect(
|
41
74
|
Some(0).match {
|
@@ -75,18 +108,14 @@ describe Deterministic::Option::Some do
|
|
75
108
|
|
76
109
|
end
|
77
110
|
|
78
|
-
describe Deterministic::Option::
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
specify { expect(described_class.new).to eq None }
|
84
|
-
specify { expect(described_class.new.some?).to be_falsey }
|
85
|
-
specify { expect(described_class.new.none?).to be_truthy }
|
86
|
-
# specify { expect { described_class.new.value }.to raise_error RuntimeError }
|
111
|
+
describe Deterministic::Option::Some do
|
112
|
+
it_behaves_like 'a Monad' do
|
113
|
+
let(:monad) { described_class }
|
114
|
+
end
|
115
|
+
end
|
87
116
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
117
|
+
describe Deterministic::Option::None do
|
118
|
+
it_behaves_like 'a Monad' do
|
119
|
+
let(:monad) { described_class }
|
120
|
+
end
|
92
121
|
end
|
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.
|
4
|
+
version: 0.13.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Zolnierek
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-08-
|
11
|
+
date: 2014-08-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -127,27 +127,28 @@ files:
|
|
127
127
|
- lib/deterministic.rb
|
128
128
|
- lib/deterministic/core_ext/object/result.rb
|
129
129
|
- lib/deterministic/core_ext/result.rb
|
130
|
+
- lib/deterministic/either.rb
|
130
131
|
- lib/deterministic/match.rb
|
131
132
|
- lib/deterministic/maybe.rb
|
132
133
|
- lib/deterministic/monad.rb
|
133
134
|
- lib/deterministic/null.rb
|
134
135
|
- lib/deterministic/option.rb
|
135
136
|
- lib/deterministic/result.rb
|
136
|
-
- lib/deterministic/result/chain.rb
|
137
137
|
- lib/deterministic/version.rb
|
138
138
|
- spec/examples/bookings_spec.rb
|
139
139
|
- spec/examples/config_spec.rb
|
140
140
|
- spec/examples/validate_address_spec.rb
|
141
141
|
- spec/lib/deterministic/core_ext/object/either_spec.rb
|
142
142
|
- spec/lib/deterministic/core_ext/result_spec.rb
|
143
|
+
- spec/lib/deterministic/either_spec.rb
|
143
144
|
- spec/lib/deterministic/maybe_spec.rb
|
144
145
|
- spec/lib/deterministic/monad_axioms.rb
|
145
146
|
- spec/lib/deterministic/monad_spec.rb
|
146
147
|
- spec/lib/deterministic/null_spec.rb
|
147
148
|
- spec/lib/deterministic/option_spec.rb
|
148
|
-
- spec/lib/deterministic/result/chain_spec.rb
|
149
149
|
- spec/lib/deterministic/result/failure_spec.rb
|
150
150
|
- spec/lib/deterministic/result/match_spec.rb
|
151
|
+
- spec/lib/deterministic/result/result_map.rb
|
151
152
|
- spec/lib/deterministic/result/result_shared.rb
|
152
153
|
- spec/lib/deterministic/result/success_spec.rb
|
153
154
|
- spec/lib/deterministic/result_spec.rb
|
@@ -182,14 +183,15 @@ test_files:
|
|
182
183
|
- spec/examples/validate_address_spec.rb
|
183
184
|
- spec/lib/deterministic/core_ext/object/either_spec.rb
|
184
185
|
- spec/lib/deterministic/core_ext/result_spec.rb
|
186
|
+
- spec/lib/deterministic/either_spec.rb
|
185
187
|
- spec/lib/deterministic/maybe_spec.rb
|
186
188
|
- spec/lib/deterministic/monad_axioms.rb
|
187
189
|
- spec/lib/deterministic/monad_spec.rb
|
188
190
|
- spec/lib/deterministic/null_spec.rb
|
189
191
|
- spec/lib/deterministic/option_spec.rb
|
190
|
-
- spec/lib/deterministic/result/chain_spec.rb
|
191
192
|
- spec/lib/deterministic/result/failure_spec.rb
|
192
193
|
- spec/lib/deterministic/result/match_spec.rb
|
194
|
+
- spec/lib/deterministic/result/result_map.rb
|
193
195
|
- spec/lib/deterministic/result/result_shared.rb
|
194
196
|
- spec/lib/deterministic/result/success_spec.rb
|
195
197
|
- spec/lib/deterministic/result_spec.rb
|
@@ -1,27 +0,0 @@
|
|
1
|
-
module Deterministic
|
2
|
-
class Result
|
3
|
-
module Chain
|
4
|
-
def map(proc=nil, &block)
|
5
|
-
return self if failure?
|
6
|
-
bind(proc || block)
|
7
|
-
end
|
8
|
-
|
9
|
-
alias :>> :map
|
10
|
-
|
11
|
-
def map_err(proc=nil, &block)
|
12
|
-
return self if success?
|
13
|
-
bind(proc || block)
|
14
|
-
end
|
15
|
-
|
16
|
-
def try(proc=nil, &block)
|
17
|
-
map(proc, &block)
|
18
|
-
rescue => err
|
19
|
-
Failure(err)
|
20
|
-
end
|
21
|
-
|
22
|
-
alias :>= :try
|
23
|
-
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
File without changes
|