deterministic 0.14.1 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -17,7 +17,7 @@ class Logger
17
17
  def validate(item)
18
18
  return Failure(["Item cannot be empty"]) if item.blank?
19
19
  return Failure(["Item must be a Hash"]) unless item.is_a?(Hash)
20
-
20
+
21
21
  validate_required_params(item).match {
22
22
  none { Success(item) }
23
23
  some { |errors| Failure(errors) }
@@ -46,7 +46,11 @@ class Logger
46
46
  class Ensure
47
47
  include Deterministic
48
48
  include Deterministic::Monad
49
- None = Deterministic::Option::None.instance
49
+
50
+ None = Deterministic::Option::None.new
51
+ def Some(value)
52
+ Option::Some.new(value)
53
+ end
50
54
 
51
55
  attr_accessor :value
52
56
 
@@ -81,34 +85,35 @@ class Ensure
81
85
  end
82
86
 
83
87
  class Validator < Ensure
84
-
88
+
85
89
  def date_is_one!
86
90
  value[:date] == 1 ? None : Some({actual: value[:date], expected: 1})
87
91
  end
88
92
 
89
93
  def required_params!
90
94
  params = %w(date tenant contract user facility short data)
91
- params.inject(None) { |errors, param|
95
+ params.inject(None) { |errors, param|
92
96
  errors + (value[:param].nil? || value[:param].empty? ? Some([param]) : None)
93
97
  }
94
98
  end
95
99
 
96
100
  def call
97
- not_empty + is_a(Array) + None + has_key(:tenant) + Some("error").value_to_a + date_is_one + required_params
101
+ not_empty + is_a(Array) + None + has_key(:tenant) + Some(["error"]) #+ date_is_one + required_params
98
102
  end
99
103
 
100
104
  end
101
105
 
102
106
  describe Ensure do
103
- # None = Deterministic::Option::None.instance
104
-
107
+ None = Deterministic::Option::None.new
108
+ Some = Deterministic::Option::Some
109
+
105
110
  it "Ensure" do
106
111
  params = {date: 2}
107
112
 
108
113
  v = Validator.new(params)
109
114
 
110
115
  errors = v.call
111
- expect(errors).to be_a Deterministic::Some
116
+ expect(errors).to be_a Some
112
117
  expect(errors.value).not_to be_empty
113
118
  end
114
119
  end
@@ -1,10 +1,10 @@
1
1
  require 'spec_helper'
2
- require 'deterministic'
3
2
 
4
- include Deterministic
5
3
 
6
4
  # A Unit of Work for validating an address
7
5
  module ValidateAddress
6
+ extend Deterministic::Prelude::Result
7
+
8
8
  def self.call(candidate)
9
9
  errors = {}
10
10
  errors[:street] = "Street cannot be empty" unless candidate.has_key? :street
@@ -16,16 +16,17 @@ module ValidateAddress
16
16
  end
17
17
 
18
18
  describe ValidateAddress do
19
+ include Deterministic
19
20
  subject { ValidateAddress.call(candidate) }
20
21
  context 'sunny day' do
21
22
  let(:candidate) { {title: "Hobbiton", street: "501 Buckland Rd", city: "Matamata", postal: "3472", country: "nz"} }
22
- specify { expect(subject).to be_a Result::Success }
23
+ specify { expect(subject).to be_a Deterministic::Result::Success }
23
24
  specify { expect(subject.value).to eq candidate }
24
25
  end
25
26
 
26
27
  context 'empty data' do
27
28
  let(:candidate) { {} }
28
- specify { expect(subject).to be_a Result::Failure }
29
+ specify { expect(subject).to be_a Deterministic::Result::Failure }
29
30
  specify { expect(subject.value).to include(:street, :city, :postal) }
30
31
  end
31
32
  end
@@ -1,14 +1,13 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe 'Class Mixin' do
4
-
5
4
  describe 'try' do
6
5
  module MyApp
7
6
  class Thing
8
- include Deterministic
7
+ include Deterministic::Prelude::Result
9
8
 
10
9
  def run
11
- Success(11) >= method(:double)
10
+ Success(11) >> method(:double)
12
11
  end
13
12
 
14
13
  def double(num)
@@ -19,7 +18,7 @@ describe 'Class Mixin' do
19
18
 
20
19
  it "cleanly mixes into a class" do
21
20
  result = MyApp::Thing.new.run
22
- expect(result).to eq Deterministic::Success.new(22)
21
+ expect(result).to eq Deterministic::Result::Success.new(22)
23
22
  end
24
23
  end
25
24
  end
@@ -3,6 +3,8 @@ require "deterministic"
3
3
  require "deterministic/core_ext/result"
4
4
 
5
5
  describe Deterministic::CoreExt::Result do
6
+ include Deterministic::Prelude::Result
7
+
6
8
  it "does something" do
7
9
  h = {}
8
10
  h.extend(Deterministic::CoreExt::Result)
@@ -12,10 +14,10 @@ describe Deterministic::CoreExt::Result do
12
14
  end
13
15
 
14
16
  it "enables #success?, #failure?, #result? on all Objects" do
15
- ary = [Deterministic::Success(true), Deterministic::Success(1)]
17
+ ary = [Success(true), Success(1)]
16
18
  expect(ary.all?(&:success?)).to be_truthy
17
19
 
18
- ary = [Deterministic::Success(true), Deterministic::Failure(1)]
20
+ ary = [Success(true), Failure(1)]
19
21
  expect(ary.all?(&:success?)).to be_falsey
20
22
  expect(ary.any?(&:failure?)).to be_truthy
21
23
  end
@@ -0,0 +1,88 @@
1
+ require 'spec_helper'
2
+
3
+ module Deterministic
4
+ module Currify
5
+ module ClassMethods
6
+ def currify(*names)
7
+ names.each { |name|
8
+ unbound_method = instance_method(name)
9
+
10
+ define_method(name) { |*args|
11
+ curried_method = unbound_method.bind(self).to_proc.curry
12
+ curried_method[*args]
13
+ }
14
+ }
15
+ end
16
+ end
17
+
18
+ def self.included(curried)
19
+ curried.extend ClassMethods
20
+ end
21
+
22
+ end
23
+ end
24
+
25
+ class ::Proc
26
+ def self.compose(f, g)
27
+ lambda { |*args| f[g[*args]] }
28
+ end
29
+
30
+ # Compose left to right
31
+ def |(g)
32
+ Proc.compose(g, self)
33
+ end
34
+
35
+ # Compose right to left
36
+ def *(g)
37
+ Proc.compose(self, g)
38
+ end
39
+ end
40
+
41
+ class Booking
42
+ include Deterministic::Currify
43
+ include Deterministic::Prelude::Result
44
+
45
+ def initialize(deps)
46
+ @deps = deps
47
+ end
48
+
49
+ def build(id, format)
50
+ validate(id) | req | find | render(format)
51
+
52
+ validate(id) | rq = request(id) | find()
53
+ end
54
+
55
+ def validate(id)
56
+ p [:validate, id]
57
+ Success(id)
58
+ end
59
+
60
+ def req(a, id)
61
+ p [:req, a, id]
62
+ Success(id: id + a)
63
+ end
64
+
65
+ def find(req)
66
+ Success({ found: req})
67
+ end
68
+
69
+ def render(format, req)
70
+ Success("rendered in #{format}: #{req[:found]}")
71
+ end
72
+
73
+ currify :find, :render, :req
74
+
75
+ end
76
+
77
+ describe "Pref" do
78
+ include Deterministic::Prelude::Result
79
+
80
+ it "does something" do
81
+ b = Booking.new(1)
82
+ actual = b.validate(1) >> b.req(2) >> b.find >> b.render(:html)
83
+
84
+ p [:actual, actual]
85
+ expected = Deterministic::Result::Success.new("rendered in html: {:id=>3}")
86
+ expect(actual).to eq expected
87
+ end
88
+ end
@@ -3,12 +3,12 @@ require_relative 'monad_axioms'
3
3
 
4
4
 
5
5
  describe Deterministic::Monad do
6
- class Identity
6
+ class Identity
7
7
  include Deterministic::Monad
8
8
  end
9
9
 
10
10
  let(:monad) { Identity }
11
- it_behaves_like 'a Monad' do
11
+ it_behaves_like 'a Monad' do
12
12
  # let(:monad) { monad }
13
13
  end
14
14
 
@@ -21,7 +21,7 @@ describe Deterministic::Monad do
21
21
  specify { expect(Identity.new('foo').fmap(&:upcase)).to eq Identity.new('FOO')}
22
22
 
23
23
  context '#bind' do
24
- it "raises an error if the passed function does not return a monad of the same class" do
24
+ it "raises an error if the passed function does not return a monad of the same class" do
25
25
  expect { Identity.new(1).bind {} }.to raise_error(Deterministic::Monad::NotMonadError)
26
26
  end
27
27
  specify { expect(Identity.new(1).bind {|value| Identity.new(value) }).to eq Identity.new(1) }
@@ -1,131 +1,140 @@
1
1
  require 'spec_helper'
2
- require_relative 'monad_axioms'
3
2
 
3
+ include Deterministic
4
4
 
5
5
  describe Deterministic::Option do
6
- include Deterministic
7
- Some = Deterministic::Some
8
- None = Deterministic::None
9
-
10
- # nil?
11
- specify { expect(described_class.some?(nil)).to eq None }
12
- specify { expect(described_class.some?(1)).to be_some }
13
- specify { expect(described_class.some?(1)).to eq Some(1) }
14
-
15
- # any?
16
- specify { expect(described_class.any?(nil)).to be_none }
17
- specify { expect(described_class.any?(None)).to be_none }
18
- specify { expect(described_class.any?("")).to be_none }
19
- specify { expect(described_class.any?([])).to be_none }
20
- specify { expect(described_class.any?({})).to be_none }
21
- specify { expect(described_class.any?([1])).to eq Some([1]) }
22
- specify { expect(described_class.any?({foo: 1})).to eq Some({foo: 1}) }
23
-
24
- # try!
25
- specify { expect(described_class.try! { raise "error" }).to be_none }
26
- end
6
+ include Deterministic::Prelude::Option
7
+ None = Deterministic::Prelude::Option::None
27
8
 
28
- describe Deterministic::Option do
29
- include Deterministic
30
- Option = Deterministic::Option
31
-
32
- # it_behaves_like 'a Monad' do
33
- # let(:monad) { described_class }
34
- # end
35
-
36
- specify { expect(Option::Some.new(0)).to be_a Option::Some }
37
- specify { expect(Option::Some.new(0)).to eq Some(0) }
38
-
39
- specify { expect(Option::None.new).to eq Option::None.new }
40
- specify { expect(Option::None.new).to eq None }
41
-
42
- # some?, none?
43
- specify { expect(None.some?).to be_falsey }
44
- specify { expect(None.none?).to be_truthy }
45
- specify { expect(Some(0).some?).to be_truthy }
46
- specify { expect(Some(0).none?).to be_falsey }
47
-
48
- # value, value_or
49
- specify { expect(Some(0).value).to eq 0 }
50
- specify { expect(Some(1).value_or(2)).to eq 1}
51
- specify { expect { None.value }.to raise_error NoMethodError }
52
- specify { expect(None.value_or(2)).to eq 2}
53
-
54
- # fmap
55
- specify { expect(Some(1).fmap { |n| n + 1}).to eq Some(2) }
56
- specify { expect(None.fmap { |n| n + 1}).to eq None }
57
-
58
- # map
59
- specify { expect(Some(1).map { |n| Some(n + 1)}).to eq Some(2) }
60
- specify { expect(Some(1).map { |n| None }).to eq None }
61
- specify { expect(None.map { |n| nil }).to eq None }
62
-
63
- # to_a
64
- specify { expect(Some(1).value_to_a). to eq Some([1])}
65
- specify { expect(Some([1]).value_to_a). to eq Some([1])}
66
- specify { expect(None.value_to_a). to eq None}
67
-
68
- # +
69
- specify { expect(Some(1) + None).to eq Some(1) }
70
- specify { expect(Some(1) + None + None).to eq Some(1) }
71
- specify { expect(Some(1) + Some(1)).to eq Some(2) }
72
- specify { expect(None + Some(1)).to eq Some(1) }
73
- specify { expect(None + None + Some(1)).to eq Some(1) }
74
- specify { expect(None + None + Some(1) + None).to eq Some(1) }
75
- specify { expect { Some([1]) + Some(1)}.to raise_error TypeError}
76
-
77
- # match
78
- specify {
9
+ specify { expect(described_class::Some.new(0)).to be_a described_class::Some }
10
+ specify { expect(described_class::Some.new(0)).to be_a described_class }
11
+ specify { expect(described_class::Some.new(0)).to eq Some(0) }
12
+
13
+ specify { expect(described_class::None.new).to eq described_class::None.new }
14
+ specify { expect(described_class::None.new).to be_a described_class::None }
15
+ specify { expect(described_class::None.new).to be_a described_class }
16
+ specify { expect(described_class::None.new).to eq None }
17
+
18
+ it "join" do
19
+ expect(Some(Some(1))).to eq Some(1)
20
+ end
21
+
22
+ it "fmap" do
23
+ expect(Some(1).fmap { |n| n + 1}).to eq Some(2)
24
+ expect(None.fmap { |n| n + 1}).to eq None
25
+ end
26
+
27
+ it "map" do
28
+ expect(Some(1).map { |n| Some(n + 1)}).to eq Some(2)
29
+ expect(Some(1).map { |n| None }).to eq None
30
+ expect(None.map { |n| Some(n + 1)}).to eq None
31
+ end
32
+
33
+ it "some?" do
34
+ expect(Some(1).some?).to be_truthy
35
+ expect(None.some?).to be_falsey
36
+ end
37
+
38
+ it "none?" do
39
+ expect(None.none?).to be_truthy
40
+ expect(Some(1).none?).to be_falsey
41
+ end
42
+
43
+ it "value" do
44
+ expect(Some(1).value).to eq 1
45
+ expect{ None.value }.to raise_error NoMethodError
46
+ end
47
+
48
+ it "value_or" do
49
+ expect(Some(1).value_or(2)).to eq 1
50
+ expect(None.value_or(0)).to eq 0
51
+ end
52
+
53
+ it "+" do
54
+ expect(Some([1]) + None).to eq Some([1])
55
+ expect(Some(1) + None + None).to eq Some(1)
56
+ expect(Some(1) + Some(1)).to eq Some(2)
57
+ expect(None + Some(1)).to eq Some(1)
58
+ expect(None + None + Some(1)).to eq Some(1)
59
+ expect(None + None + Some(1) + None).to eq Some(1)
60
+ expect(None + Some({foo: 1})).to eq Some({:foo=>1})
61
+ expect(Some([1]) + Some([1])).to eq Some([1, 1])
62
+ expect { Some([1]) + Some(1)}.to raise_error TypeError
63
+ end
64
+
65
+ it "inspect" do
66
+ expect(Some(1).inspect).to eq "Some(1)"
67
+ expect(described_class::None.new.inspect).to eq "None"
68
+ end
69
+
70
+ it "to_s" do
71
+ expect(Some(1).to_s).to eq "1"
72
+ expect(described_class::None.new.to_s).to eq ""
73
+ end
74
+
75
+ it "match" do
79
76
  expect(
80
77
  Some(0).match {
81
- some(1) { |n| 99 }
82
- some(0) { |n| n + 1 }
83
- none(1) {}
78
+ Some(s, where { s == 1 } ) { |n| 99 }
79
+ Some(s, where { s == 0 }) { |n| s + 1 }
80
+ None() {}
84
81
  }
85
82
  ).to eq 1
86
- }
87
83
 
88
- specify {
89
84
  expect(
90
- Some(nil).match {
91
- none { 0 }
92
- some { 1 }
85
+ Some(1).match {
86
+ None() { 0 }
87
+ Some(s) { 1 }
93
88
  }
94
89
  ).to eq 1
95
- }
96
90
 
97
- specify {
98
91
  expect(
99
92
  Some(1).match {
100
- none { 0 }
101
- some(Fixnum) { 1 }
93
+ None() { 0 }
94
+ Some(s, where { s.is_a? Fixnum }) { 1 }
102
95
  }
103
96
  ).to eq 1
104
- }
105
97
 
106
- specify {
107
98
  expect(
108
99
  None.match {
109
- none { 0 }
110
- some { 1 }
100
+ None() { 0 }
101
+ Some() { 1 }
111
102
  }
112
103
  ).to eq 0
113
- }
104
+ end
114
105
 
115
- end
106
+ it "nil?" do
107
+ expect(described_class.some?(nil)).to eq None
108
+ expect(described_class.some?(1)).to be_some
109
+ expect(described_class.some?(1)).to eq Some(1)
110
+ end
116
111
 
117
- describe Deterministic::Option::Some do
118
- include Deterministic
112
+ it "any?" do
113
+ expect(described_class.any?(nil)).to be_none
114
+ expect(described_class.any?(None)).to be_none
115
+ expect(described_class.any?("")).to be_none
116
+ expect(described_class.any?([])).to be_none
117
+ expect(described_class.any?({})).to be_none
118
+ expect(described_class.any?([1])).to eq Some([1])
119
+ expect(described_class.any?({foo: 1})).to eq Some({foo: 1})
120
+ end
119
121
 
120
- it_behaves_like 'a Monad' do
121
- let(:monad) { described_class }
122
+ it "try!" do
123
+ expect(described_class.try! { raise "error" }).to be_none
122
124
  end
123
125
  end
124
126
 
125
- describe Deterministic::Option::None do
126
- include Deterministic
127
+ require_relative 'monad_axioms'
127
128
 
128
- it_behaves_like 'a Monad' do
129
+ describe Deterministic::Option::Some do
130
+ it_behaves_like 'a Monad' do
129
131
  let(:monad) { described_class }
130
132
  end
131
133
  end
134
+
135
+ # describe Deterministic::Option::None do
136
+ # it_behaves_like 'a Monad' do
137
+
138
+ # let(:monad) { described_class }
139
+ # end
140
+ # end