deterministic 0.5.2 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +27 -2
- data/lib/deterministic.rb +1 -0
- data/lib/deterministic/either.rb +8 -0
- data/lib/deterministic/either/failure.rb +17 -0
- data/lib/deterministic/either/success.rb +17 -0
- data/lib/deterministic/maybe.rb +0 -53
- data/lib/deterministic/monad.rb +1 -1
- data/lib/deterministic/none.rb +72 -0
- data/lib/deterministic/version.rb +1 -1
- data/spec/lib/deterministic/either/failure_spec.rb +27 -0
- data/spec/lib/deterministic/maybe_spec.rb +0 -31
- data/spec/lib/deterministic/none_spec.rb +49 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a2ce59cd264bdbafaa4a86660553c4e407b6607
|
4
|
+
data.tar.gz: d3418defe22825466d5c4bdb7c7363f53c767442
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6226df9e5bef50b905208606a0634aa94b8ca70d61be98318a02ffe6da40bb90ddec4304b6ccfa0a844d5230ab9743d25fa9997c65e5fc64e378e9505e45f08f
|
7
|
+
data.tar.gz: 6b3d7eeb06e263853cdad760a5df74043981f914be4e69ed80b10c37618ce1d6e9134e01a65675ce305d2fc6fa15b7c380dbf3cc760902769f6c9ba1e8e8e227
|
data/README.md
CHANGED
@@ -22,8 +22,9 @@ Failure(1) << Failure(2) # => Failure(1)
|
|
22
22
|
Failure(Failure(1)) # => Failure(1)
|
23
23
|
Failure(1).map { |v| v + 1} # => Failure(2)
|
24
24
|
Failure({a:1}).to_json # => '{"Failure": {"a":1}}'
|
25
|
+
```
|
25
26
|
|
26
|
-
### Either
|
27
|
+
### Either.attempt_all
|
27
28
|
The basic idea is to execute a chain of units of work and make sure all return either `Success` or `Failure`.
|
28
29
|
|
29
30
|
```ruby
|
@@ -149,6 +150,30 @@ Success(1).match do
|
|
149
150
|
end # => "catch-all"
|
150
151
|
```
|
151
152
|
|
153
|
+
## Flow control
|
154
|
+
|
155
|
+
Chaininig successfull actions
|
156
|
+
|
157
|
+
```ruby
|
158
|
+
Success(1).and Success(2) # => Success(2)
|
159
|
+
Success(1).and_then { Success(2) } # => Success(2)
|
160
|
+
|
161
|
+
Success(1).or Success(2) # => Success(1)
|
162
|
+
Success(1).or_else { Success(2) } # => Success(1)
|
163
|
+
```
|
164
|
+
|
165
|
+
Chaininig failed actions
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
Failure(1).and Success(2) # => Failure(1)
|
169
|
+
Failure(1).and_then { Success(2) } # => Failure(1)
|
170
|
+
|
171
|
+
Failure(1).or Success(1) # => Success(1)
|
172
|
+
Failure(1).or_else { |v| Success(v)} # => Success(1)
|
173
|
+
```
|
174
|
+
|
175
|
+
The value or block result must always be a `Success` or `Failure`
|
176
|
+
|
152
177
|
## core_ext
|
153
178
|
You can use a core extension, to include Either in your own class or in Object, i.e. in all classes.
|
154
179
|
|
@@ -222,7 +247,7 @@ Maybe({}).some? # => true
|
|
222
247
|
|
223
248
|
## Mimic
|
224
249
|
|
225
|
-
If you want a custom NullObject which mimicks another class
|
250
|
+
If you want a custom NullObject which mimicks another class.
|
226
251
|
|
227
252
|
```ruby
|
228
253
|
class Mimick
|
data/lib/deterministic.rb
CHANGED
data/lib/deterministic/either.rb
CHANGED
@@ -1,8 +1,15 @@
|
|
1
1
|
module Deterministic
|
2
|
+
# Abstract parent of Success and Failure
|
2
3
|
class Either
|
3
4
|
include Monad
|
4
5
|
include Deterministic::PatternMatching
|
5
6
|
|
7
|
+
def bind(proc=nil, &block)
|
8
|
+
(proc || block).call(value, self.class).tap do |result|
|
9
|
+
raise NotMonadError, "Expected #{result.inspect} to be an Either" unless result.is_a? self.class.superclass
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
6
13
|
def success?
|
7
14
|
is_a? Success
|
8
15
|
end
|
@@ -16,6 +23,7 @@ module Deterministic
|
|
16
23
|
return other if other.is_a? Either
|
17
24
|
end
|
18
25
|
|
26
|
+
# This is an abstract class, can't ever instantiate it directly
|
19
27
|
class << self
|
20
28
|
protected :new
|
21
29
|
end
|
@@ -1,5 +1,22 @@
|
|
1
1
|
module Deterministic
|
2
2
|
class Failure < Either
|
3
3
|
class << self; public :new; end
|
4
|
+
|
5
|
+
def and(other)
|
6
|
+
self
|
7
|
+
end
|
8
|
+
|
9
|
+
def and_then(&block)
|
10
|
+
self
|
11
|
+
end
|
12
|
+
|
13
|
+
def or(other)
|
14
|
+
raise NotMonadError, "Expected #{other.inspect} to be an Either" unless other.is_a? Either
|
15
|
+
other
|
16
|
+
end
|
17
|
+
|
18
|
+
def or_else(&block)
|
19
|
+
bind(&block)
|
20
|
+
end
|
4
21
|
end
|
5
22
|
end
|
@@ -1,5 +1,22 @@
|
|
1
1
|
module Deterministic
|
2
2
|
class Success < Either
|
3
3
|
class << self; public :new; end
|
4
|
+
|
5
|
+
def and(other)
|
6
|
+
raise NotMonadError, "Expected #{other.inspect} to be an Either" unless other.is_a? Either
|
7
|
+
other
|
8
|
+
end
|
9
|
+
|
10
|
+
def and_then(&block)
|
11
|
+
bind(&block)
|
12
|
+
end
|
13
|
+
|
14
|
+
def or(other)
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
def or_else(&block)
|
19
|
+
self
|
20
|
+
end
|
4
21
|
end
|
5
22
|
end
|
data/lib/deterministic/maybe.rb
CHANGED
@@ -1,56 +1,3 @@
|
|
1
|
-
# The simplest NullObject there can be
|
2
|
-
class None
|
3
|
-
class << self
|
4
|
-
def method_missing(m, *args)
|
5
|
-
if m == :new
|
6
|
-
super
|
7
|
-
else
|
8
|
-
None.instance.send(m, *args)
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
def instance
|
13
|
-
@instance ||= new([])
|
14
|
-
end
|
15
|
-
|
16
|
-
def mimic(klas)
|
17
|
-
new(klas.instance_methods(false))
|
18
|
-
end
|
19
|
-
end
|
20
|
-
private_class_method :new
|
21
|
-
|
22
|
-
def initialize(methods)
|
23
|
-
@methods = methods
|
24
|
-
end
|
25
|
-
|
26
|
-
# implicit conversions
|
27
|
-
def to_str
|
28
|
-
''
|
29
|
-
end
|
30
|
-
|
31
|
-
def to_ary
|
32
|
-
[]
|
33
|
-
end
|
34
|
-
|
35
|
-
def method_missing(m, *args)
|
36
|
-
return self if respond_to?(m)
|
37
|
-
super
|
38
|
-
end
|
39
|
-
|
40
|
-
def none?
|
41
|
-
true
|
42
|
-
end
|
43
|
-
|
44
|
-
def some?
|
45
|
-
false
|
46
|
-
end
|
47
|
-
|
48
|
-
def respond_to?(m)
|
49
|
-
return true if @methods.empty? || @methods.include?(m)
|
50
|
-
super
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
1
|
class Object
|
55
2
|
def none?
|
56
3
|
false
|
data/lib/deterministic/monad.rb
CHANGED
@@ -26,7 +26,7 @@ module Deterministic
|
|
26
26
|
# the self.class, i.e. the containing monad is passed as a second (optional) arg to the function
|
27
27
|
def bind(proc=nil, &block)
|
28
28
|
(proc || block).call(value, self.class).tap do |result|
|
29
|
-
raise NotMonadError unless result.is_a? self.class
|
29
|
+
raise NotMonadError, "Expected #{result.inspect} to be an Either" unless result.is_a? self.class
|
30
30
|
end
|
31
31
|
end
|
32
32
|
alias :'>>=' :bind
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# The simplest NullObject there can be
|
2
|
+
class None
|
3
|
+
class << self
|
4
|
+
def method_missing(m, *args)
|
5
|
+
if m == :new
|
6
|
+
super
|
7
|
+
else
|
8
|
+
None.instance.send(m, *args)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def instance
|
13
|
+
@instance ||= new([])
|
14
|
+
end
|
15
|
+
|
16
|
+
def none?
|
17
|
+
true
|
18
|
+
end
|
19
|
+
|
20
|
+
def some?
|
21
|
+
false
|
22
|
+
end
|
23
|
+
|
24
|
+
def mimic(klas)
|
25
|
+
new(klas.instance_methods(false))
|
26
|
+
end
|
27
|
+
|
28
|
+
def ==(other)
|
29
|
+
other.respond_to?(:none?) && other.none?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
private_class_method :new
|
33
|
+
|
34
|
+
def initialize(methods)
|
35
|
+
@methods = methods
|
36
|
+
end
|
37
|
+
|
38
|
+
# implicit conversions
|
39
|
+
def to_str
|
40
|
+
''
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_ary
|
44
|
+
[]
|
45
|
+
end
|
46
|
+
|
47
|
+
def method_missing(m, *args)
|
48
|
+
return self if respond_to?(m)
|
49
|
+
super
|
50
|
+
end
|
51
|
+
|
52
|
+
def none?
|
53
|
+
true
|
54
|
+
end
|
55
|
+
|
56
|
+
def some?
|
57
|
+
false
|
58
|
+
end
|
59
|
+
|
60
|
+
def respond_to?(m)
|
61
|
+
return true if @methods.empty? || @methods.include?(m)
|
62
|
+
super
|
63
|
+
end
|
64
|
+
|
65
|
+
def inspect
|
66
|
+
'None'
|
67
|
+
end
|
68
|
+
|
69
|
+
def ==(other)
|
70
|
+
other.respond_to?(:none?) && other.none?
|
71
|
+
end
|
72
|
+
end
|
@@ -26,3 +26,30 @@ describe Deterministic::Failure do
|
|
26
26
|
let(:either) { described_class }
|
27
27
|
end
|
28
28
|
end
|
29
|
+
|
30
|
+
|
31
|
+
describe "Chaining" do
|
32
|
+
it "#or" do
|
33
|
+
expect(Success(1).or(Failure(2))).to eq Success(1)
|
34
|
+
expect(Failure(1).or(Success(2))).to eq Success(2)
|
35
|
+
expect { Failure(1).or(2) }.to raise_error(Deterministic::Monad::NotMonadError)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "#or_else" do
|
39
|
+
expect(Success(1).or_else { Failure(2) }).to eq Success(1)
|
40
|
+
expect(Failure(1).or_else { |v| Success(v + 1) }).to eq Success(2)
|
41
|
+
expect { Failure(1).or_else { 2 } }.to raise_error(Deterministic::Monad::NotMonadError)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "#and" do
|
45
|
+
expect(Success(1).and(Success(2))).to eq Success(2)
|
46
|
+
expect(Failure(1).and(Success(2))).to eq Failure(1)
|
47
|
+
expect { Success(1).and(2) }.to raise_error(Deterministic::Monad::NotMonadError)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "#and_then" do
|
51
|
+
expect(Success(1).and_then { Success(2) }).to eq Success(2)
|
52
|
+
expect(Failure(1).and_then { Success(2) }).to eq Failure(1)
|
53
|
+
expect { Success(1).and_then { 2 } }.to raise_error(Deterministic::Monad::NotMonadError)
|
54
|
+
end
|
55
|
+
end
|
@@ -13,35 +13,4 @@ describe 'maybe' do
|
|
13
13
|
expect(Maybe("a")).not_to be_none
|
14
14
|
end
|
15
15
|
|
16
|
-
it "None is a Singleton" do
|
17
|
-
expect(None.instance).to be_a None
|
18
|
-
expect { None.new }.to raise_error(NoMethodError, "private method `new' called for None:Class")
|
19
|
-
end
|
20
|
-
|
21
|
-
it "implicit conversions" do
|
22
|
-
null = Maybe(nil)
|
23
|
-
expect(null.to_str).to eq ""
|
24
|
-
expect(null.to_ary).to eq []
|
25
|
-
expect("" + null).to eq ""
|
26
|
-
|
27
|
-
a, b, c = null
|
28
|
-
expect(a).to be_nil
|
29
|
-
expect(b).to be_nil
|
30
|
-
expect(c).to be_nil
|
31
|
-
end
|
32
|
-
|
33
|
-
it "explicit conversions" do
|
34
|
-
expect(None.to_s).to eq 'None'
|
35
|
-
end
|
36
|
-
|
37
|
-
it "mimic, only return None on specific methods of another class" do
|
38
|
-
class MimicTest
|
39
|
-
def test
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
mimic = None.mimic(MimicTest)
|
44
|
-
expect(mimic.test).to be_none
|
45
|
-
expect { mimic.foo }.to raise_error(NoMethodError)
|
46
|
-
end
|
47
16
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "deterministic/none"
|
3
|
+
|
4
|
+
describe None do
|
5
|
+
it "None is a Singleton" do
|
6
|
+
expect(None.instance).to be_a None
|
7
|
+
expect { None.new }.to raise_error(NoMethodError, "private method `new' called for None:Class")
|
8
|
+
end
|
9
|
+
|
10
|
+
it "explicit conversions" do
|
11
|
+
expect(None.to_s).to eq 'None'
|
12
|
+
expect(None.inspect).to eq 'None'
|
13
|
+
end
|
14
|
+
|
15
|
+
it "compares to None" do
|
16
|
+
expect(None === None.instance).to be_truthy
|
17
|
+
expect(None.instance === None).to be_truthy
|
18
|
+
expect(None.instance).to eq None
|
19
|
+
expect(None).to eq None.instance
|
20
|
+
expect(1).not_to be None
|
21
|
+
expect(1).not_to be None.instance
|
22
|
+
expect(None.instance).not_to be 1
|
23
|
+
expect(None).not_to be 1
|
24
|
+
expect(None.instance).not_to be_nil
|
25
|
+
expect(None).not_to be_nil
|
26
|
+
end
|
27
|
+
|
28
|
+
it "implicit conversions" do
|
29
|
+
none = None.instance
|
30
|
+
expect(none.to_str).to eq ""
|
31
|
+
expect(none.to_ary).to eq []
|
32
|
+
expect("" + none).to eq ""
|
33
|
+
|
34
|
+
a, b, c = none
|
35
|
+
expect(a).to be_nil
|
36
|
+
expect(b).to be_nil
|
37
|
+
expect(c).to be_nil
|
38
|
+
end
|
39
|
+
|
40
|
+
it "mimicks other classes and returns None for their public methods" do
|
41
|
+
class UnderMimickTest
|
42
|
+
def test; end
|
43
|
+
end
|
44
|
+
|
45
|
+
mimick = None.mimic(UnderMimickTest)
|
46
|
+
expect(mimick.test).to be_none
|
47
|
+
expect { mimick.i_dont_exist}.to raise_error(NoMethodError)
|
48
|
+
end
|
49
|
+
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.6.0
|
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-03-
|
11
|
+
date: 2014-03-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -134,6 +134,7 @@ files:
|
|
134
134
|
- lib/deterministic/either/success.rb
|
135
135
|
- lib/deterministic/maybe.rb
|
136
136
|
- lib/deterministic/monad.rb
|
137
|
+
- lib/deterministic/none.rb
|
137
138
|
- lib/deterministic/version.rb
|
138
139
|
- spec/examples/bookings_spec.rb
|
139
140
|
- spec/examples/validate_address_spec.rb
|
@@ -148,6 +149,7 @@ files:
|
|
148
149
|
- spec/lib/deterministic/maybe_spec.rb
|
149
150
|
- spec/lib/deterministic/monad_axioms.rb
|
150
151
|
- spec/lib/deterministic/monad_spec.rb
|
152
|
+
- spec/lib/deterministic/none_spec.rb
|
151
153
|
- spec/spec_helper.rb
|
152
154
|
homepage: http://github.com/pzol/deterministic
|
153
155
|
licenses:
|
@@ -187,4 +189,5 @@ test_files:
|
|
187
189
|
- spec/lib/deterministic/maybe_spec.rb
|
188
190
|
- spec/lib/deterministic/monad_axioms.rb
|
189
191
|
- spec/lib/deterministic/monad_spec.rb
|
192
|
+
- spec/lib/deterministic/none_spec.rb
|
190
193
|
- spec/spec_helper.rb
|