deterministic 0.5.2 → 0.6.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
- 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
|