deterministic 0.1.2 → 0.2.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 +56 -3
- data/deterministic.gemspec +1 -1
- data/lib/deterministic/core_ext/either.rb +23 -0
- data/lib/deterministic/core_ext/object/either.rb +6 -0
- data/lib/deterministic/either/attempt_all.rb +4 -4
- data/lib/deterministic/either/match.rb +4 -3
- data/lib/deterministic/version.rb +1 -1
- data/spec/lib/deterministic/attempt_all_spec.rb +1 -1
- data/spec/lib/deterministic/core_ext/either_spec.rb +50 -0
- data/spec/lib/deterministic/core_ext/object/either_spec.rb +28 -0
- data/spec/lib/deterministic/either/match_spec.rb +33 -2
- data/spec/lib/deterministic/monad_axioms.rb +1 -1
- data/spec/lib/deterministic/monad_spec.rb +0 -1
- data/spec/spec_helper.rb +2 -0
- metadata +13 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a99a96ae91c0ed68fd47e14ba991e03c1302349c
|
4
|
+
data.tar.gz: c9de7570b1ef16aab2b10ee4aa97f927d7661048
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c15b124c022ae997e29be36a63237cb50963268c6642d6f55c6f3a5e79f5de047b76182d3eb3d69b57b3a4e6ef207e9ace98b3579613154788fb706429cd750f
|
7
|
+
data.tar.gz: efe04519bfbca769720df381304134c72b4e21ebea770aabb1f0dff73dadca7bb98e0735f90ae0f569e7182fdba7ffd6d4f736ba5c238053a589e5cd55e42324
|
data/README.md
CHANGED
@@ -72,7 +72,7 @@ Success(1).match do
|
|
72
72
|
success { |v| "success #{v}"}
|
73
73
|
failure { |v| "failure #{v}"}
|
74
74
|
either { |v| "either #{v}"}
|
75
|
-
end # => "
|
75
|
+
end # => "success 1"
|
76
76
|
```
|
77
77
|
Note1: the inner value has been unwrapped!
|
78
78
|
|
@@ -84,7 +84,7 @@ Values for patterns are good, too:
|
|
84
84
|
|
85
85
|
```ruby
|
86
86
|
Success(1).match do
|
87
|
-
success(1) { "Success #{v}" }
|
87
|
+
success(1) {|v| "Success #{v}" }
|
88
88
|
end # => "Success 1"
|
89
89
|
```
|
90
90
|
|
@@ -92,7 +92,7 @@ You can and should also use procs for patterns:
|
|
92
92
|
|
93
93
|
```ruby
|
94
94
|
Success(1).match do
|
95
|
-
success ->(v) { v == 1} { "Success #{v}" }
|
95
|
+
success ->(v) { v == 1 } {|v| "Success #{v}" }
|
96
96
|
end # => "Success 1"
|
97
97
|
```
|
98
98
|
|
@@ -132,6 +132,59 @@ Success(1).match do
|
|
132
132
|
end # => "catch-all"
|
133
133
|
```
|
134
134
|
|
135
|
+
## core_ext
|
136
|
+
You can use a core extension, to include Either in your own class or in Object, i.e. in all classes.
|
137
|
+
|
138
|
+
In a class, as a mixin
|
139
|
+
|
140
|
+
```ruby
|
141
|
+
require "deterministic/core_ext/either" # this includes Deterministic in the global namespace!
|
142
|
+
class UnderTest
|
143
|
+
include Deterministic::CoreExt::Either
|
144
|
+
def test
|
145
|
+
attempt_all do
|
146
|
+
try { foo }
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def foo
|
151
|
+
1
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
ut = UnderTest.new
|
156
|
+
```
|
157
|
+
|
158
|
+
To add it to all classes
|
159
|
+
|
160
|
+
```ruby
|
161
|
+
require "deterministic/core_ext/object/either" # this includes Deterministic to Object
|
162
|
+
|
163
|
+
class UnderTest
|
164
|
+
def test
|
165
|
+
attempt_all do
|
166
|
+
try { foo }
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def foo
|
171
|
+
1
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
ut = UnderTest.new
|
176
|
+
```
|
177
|
+
|
178
|
+
or use it on built-in classes
|
179
|
+
|
180
|
+
```ruby
|
181
|
+
require "deterministic/core_ext/object/either"
|
182
|
+
h = {a:1}
|
183
|
+
h.attempt_all do
|
184
|
+
try { |s| self[:a] + 1}
|
185
|
+
end
|
186
|
+
```
|
187
|
+
|
135
188
|
## Inspirations
|
136
189
|
* My [Monadic gem](http://github.com/pzol/monadic) of course
|
137
190
|
* `#attempt_all` was somewhat inspired by [An error monad in Clojure](http://brehaut.net/blog/2011/error_monads)
|
data/deterministic.gemspec
CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
spec.add_development_dependency "bundler", "~> 1.3"
|
22
22
|
spec.add_development_dependency "rake"
|
23
|
-
spec.add_development_dependency "rspec"
|
23
|
+
spec.add_development_dependency "rspec", "~>3.0.0.beta1"
|
24
24
|
spec.add_development_dependency "guard"
|
25
25
|
spec.add_development_dependency "guard-bundler"
|
26
26
|
spec.add_development_dependency "guard-rspec"
|
@@ -0,0 +1,23 @@
|
|
1
|
+
include Deterministic
|
2
|
+
|
3
|
+
module Deterministic
|
4
|
+
module CoreExt
|
5
|
+
module Either
|
6
|
+
def success?
|
7
|
+
self.is_a? Success
|
8
|
+
end
|
9
|
+
|
10
|
+
def failure?
|
11
|
+
self.is_a? Failure
|
12
|
+
end
|
13
|
+
|
14
|
+
def either?
|
15
|
+
success? || failure?
|
16
|
+
end
|
17
|
+
|
18
|
+
def attempt_all(context=self, &block)
|
19
|
+
Deterministic::Either::AttemptAll.new(context, &block).call(self)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -14,7 +14,7 @@ class Deterministic::Either
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def call(initial=nil)
|
17
|
-
result = @tries.inject(Success(initial)) do |acc, try|
|
17
|
+
result = @tries.inject(Deterministic::Success(initial)) do |acc, try|
|
18
18
|
acc.success? ? acc << try.call(acc) : acc
|
19
19
|
end
|
20
20
|
end
|
@@ -24,9 +24,9 @@ class Deterministic::Either
|
|
24
24
|
try_p = ->(acc) {
|
25
25
|
begin
|
26
26
|
value = @context.instance_exec(acc.value, &block)
|
27
|
-
Success(value)
|
27
|
+
Deterministic::Success(value)
|
28
28
|
rescue => ex
|
29
|
-
Failure(ex)
|
29
|
+
Deterministic::Failure(ex)
|
30
30
|
end
|
31
31
|
}
|
32
32
|
|
@@ -35,7 +35,7 @@ class Deterministic::Either
|
|
35
35
|
|
36
36
|
# Basicly a monad
|
37
37
|
def let(sym=nil, &block)
|
38
|
-
@tries << ->(acc) {
|
38
|
+
@tries << ->(acc) {
|
39
39
|
@context.instance_exec(acc.value, &block).tap do |value|
|
40
40
|
raise EitherExpectedError, "Expected the result to be either Success or Failure" unless value.is_a? Either
|
41
41
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module Deterministic::PatternMatching
|
2
2
|
|
3
|
-
def match(context=
|
3
|
+
def match(context=nil, &block)
|
4
|
+
context ||= block.binding.eval('self')
|
4
5
|
match = Match.new(self, context)
|
5
6
|
match.instance_eval &block
|
6
7
|
match.result
|
@@ -35,7 +36,7 @@ module Deterministic::PatternMatching
|
|
35
36
|
end
|
36
37
|
|
37
38
|
private
|
38
|
-
Matcher = Struct.new(:condition, :block) do
|
39
|
+
Matcher = Struct.new(:condition, :block) do
|
39
40
|
def matches?(value)
|
40
41
|
condition.call(value)
|
41
42
|
end
|
@@ -46,7 +47,7 @@ module Deterministic::PatternMatching
|
|
46
47
|
when condition.nil?; ->(v) { true }
|
47
48
|
when condition.is_a?(Proc); condition
|
48
49
|
when condition.is_a?(Class); ->(v) { condition === @container.value }
|
49
|
-
else ->(v) {
|
50
|
+
else ->(v) { @container.value == condition }
|
50
51
|
end
|
51
52
|
|
52
53
|
matcher_pred = compose_predicates(type_pred[type], condition_pred)
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "deterministic"
|
3
|
+
require "deterministic/core_ext/either"
|
4
|
+
|
5
|
+
describe Deterministic::CoreExt::Either do
|
6
|
+
it "does something" do
|
7
|
+
h = {}
|
8
|
+
h.extend(Deterministic::CoreExt::Either)
|
9
|
+
expect(h.success?).to be_falsey
|
10
|
+
expect(h.failure?).to be_falsey
|
11
|
+
expect(h.either?).to be_falsey
|
12
|
+
end
|
13
|
+
|
14
|
+
it "enables #success?, #failure?, #either? on all Objects" do
|
15
|
+
ary = [Deterministic::Success(true), Deterministic::Success(1)]
|
16
|
+
expect(ary.all?(&:success?)).to be_truthy
|
17
|
+
|
18
|
+
ary = [Deterministic::Success(true), Deterministic::Failure(1)]
|
19
|
+
expect(ary.all?(&:success?)).to be_falsey
|
20
|
+
expect(ary.any?(&:failure?)).to be_truthy
|
21
|
+
end
|
22
|
+
|
23
|
+
it "allows using attempt_all on all Objects" do
|
24
|
+
h = {a: 1}
|
25
|
+
h.extend(Deterministic::CoreExt::Either)
|
26
|
+
res = h.attempt_all do
|
27
|
+
try { |s| self[:a] + 1}
|
28
|
+
end
|
29
|
+
|
30
|
+
expect(res).to eq Deterministic::Success(2)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "use attempt_all in an instance" do
|
34
|
+
class UnderTest
|
35
|
+
include Deterministic::CoreExt::Either
|
36
|
+
def test
|
37
|
+
attempt_all do
|
38
|
+
try { foo }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def foo
|
43
|
+
1
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
ut = UnderTest.new
|
48
|
+
expect(ut.test).to eq Success(1)
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "deterministic/core_ext/object/either"
|
3
|
+
|
4
|
+
describe Deterministic::CoreExt::Either, "object", isolate: true do
|
5
|
+
it "does something" do
|
6
|
+
h = {a: 1}
|
7
|
+
expect(h.success?).to be_falsey
|
8
|
+
expect(h.failure?).to be_falsey
|
9
|
+
expect(h.either?).to be_falsey
|
10
|
+
end
|
11
|
+
|
12
|
+
it "use attempt_all in an instance" do
|
13
|
+
class UnderTest
|
14
|
+
def test
|
15
|
+
attempt_all do
|
16
|
+
try { foo }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def foo
|
21
|
+
1
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
ut = UnderTest.new
|
26
|
+
expect(ut.test).to eq Success(1)
|
27
|
+
end
|
28
|
+
end
|
@@ -9,7 +9,7 @@ describe Deterministic::Either::Match do
|
|
9
9
|
success { |v| "Success #{v}" }
|
10
10
|
failure { |v| "Failure #{v}" }
|
11
11
|
end
|
12
|
-
).to eq "Success 1"
|
12
|
+
).to eq "Success 1"
|
13
13
|
end
|
14
14
|
|
15
15
|
it "can match Failure" do
|
@@ -18,7 +18,7 @@ describe Deterministic::Either::Match do
|
|
18
18
|
success { |v| "Success #{v}" }
|
19
19
|
failure { |v| "Failure #{v}" }
|
20
20
|
end
|
21
|
-
).to eq "Failure 1"
|
21
|
+
).to eq "Failure 1"
|
22
22
|
end
|
23
23
|
|
24
24
|
it "can match with values" do
|
@@ -81,4 +81,35 @@ describe Deterministic::Either::Match do
|
|
81
81
|
end
|
82
82
|
}.to raise_error Deterministic::PatternMatching::NoMatchError
|
83
83
|
end
|
84
|
+
|
85
|
+
it "can use methods from the caller's binding scope by default" do
|
86
|
+
def my_method
|
87
|
+
"it works"
|
88
|
+
end
|
89
|
+
expect(
|
90
|
+
Success(1).match do
|
91
|
+
success { my_method }
|
92
|
+
end
|
93
|
+
).to eq "it works"
|
94
|
+
end
|
95
|
+
|
96
|
+
it "allows for Either values to play with pattern matching comparisons" do
|
97
|
+
class MyErrorHash < Hash
|
98
|
+
def ==(error_type_symbol)
|
99
|
+
self[:type] == error_type_symbol
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
error_hash = MyErrorHash.new.merge(:type => :needs_more_salt, :arbitrary => 'data')
|
104
|
+
|
105
|
+
expect(
|
106
|
+
Failure(error_hash).match do
|
107
|
+
failure(:nothing) {|v| raise "We should not get to this point" }
|
108
|
+
failure(:needs_more_salt) do |error|
|
109
|
+
"Successfully failed with #{error[:arbitrary]}!"
|
110
|
+
end
|
111
|
+
failure { raise "We should also not get to this point" }
|
112
|
+
end
|
113
|
+
).to eq "Successfully failed with data!"
|
114
|
+
end
|
84
115
|
end
|
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.
|
4
|
+
version: 0.2.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-
|
11
|
+
date: 2014-03-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -42,16 +42,16 @@ dependencies:
|
|
42
42
|
name: rspec
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - ~>
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 3.0.0.beta1
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - ~>
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 3.0.0.beta1
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: guard
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -125,6 +125,8 @@ files:
|
|
125
125
|
- deterministic.gemspec
|
126
126
|
- either.rb
|
127
127
|
- lib/deterministic.rb
|
128
|
+
- lib/deterministic/core_ext/either.rb
|
129
|
+
- lib/deterministic/core_ext/object/either.rb
|
128
130
|
- lib/deterministic/either.rb
|
129
131
|
- lib/deterministic/either/attempt_all.rb
|
130
132
|
- lib/deterministic/either/failure.rb
|
@@ -135,6 +137,8 @@ files:
|
|
135
137
|
- spec/examples/bookings_spec.rb
|
136
138
|
- spec/examples/validate_address_spec.rb
|
137
139
|
- spec/lib/deterministic/attempt_all_spec.rb
|
140
|
+
- spec/lib/deterministic/core_ext/either_spec.rb
|
141
|
+
- spec/lib/deterministic/core_ext/object/either_spec.rb
|
138
142
|
- spec/lib/deterministic/either/match_spec.rb
|
139
143
|
- spec/lib/deterministic/either/success_spec.rb
|
140
144
|
- spec/lib/deterministic/either_spec.rb
|
@@ -161,7 +165,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
161
165
|
version: '0'
|
162
166
|
requirements: []
|
163
167
|
rubyforge_project:
|
164
|
-
rubygems_version: 2.
|
168
|
+
rubygems_version: 2.2.1
|
165
169
|
signing_key:
|
166
170
|
specification_version: 4
|
167
171
|
summary: see above
|
@@ -169,6 +173,8 @@ test_files:
|
|
169
173
|
- spec/examples/bookings_spec.rb
|
170
174
|
- spec/examples/validate_address_spec.rb
|
171
175
|
- spec/lib/deterministic/attempt_all_spec.rb
|
176
|
+
- spec/lib/deterministic/core_ext/either_spec.rb
|
177
|
+
- spec/lib/deterministic/core_ext/object/either_spec.rb
|
172
178
|
- spec/lib/deterministic/either/match_spec.rb
|
173
179
|
- spec/lib/deterministic/either/success_spec.rb
|
174
180
|
- spec/lib/deterministic/either_spec.rb
|