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 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