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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9b0f292bd43ef9a647f89e10a27d7ee6c03f9c86
4
- data.tar.gz: 353997a1fa950788805f34a170b6fb83f4e84829
3
+ metadata.gz: 2a2ce59cd264bdbafaa4a86660553c4e407b6607
4
+ data.tar.gz: d3418defe22825466d5c4bdb7c7363f53c767442
5
5
  SHA512:
6
- metadata.gz: 943ebb00ce456140da6bf042681b53383fa19fa84ca529f7852d0f0671d089d203b64152a42dd897ec7a1f2a2fca9a646e4aff0bb945bf1fe07ee5963835d73a
7
- data.tar.gz: 2f9e20201ac9c8d9fd19672eb7abe5f27a12d156dce4b7b49d6244a1a4363fef7e0344564f0b876a330bf4cd7064c9d40b9d79de940f310a156e30357bbea021
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#attempt_all
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
@@ -10,3 +10,4 @@ require 'deterministic/either'
10
10
  require 'deterministic/either/attempt_all'
11
11
  require 'deterministic/either/success'
12
12
  require 'deterministic/either/failure'
13
+ require 'deterministic/none'
@@ -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
@@ -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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Deterministic
2
- VERSION = "0.5.2"
2
+ VERSION = "0.6.0"
3
3
  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.5.2
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-29 00:00:00.000000000 Z
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