deterministic 0.10.0 → 0.12.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: d6ccf85126958f17b98ac22932b29ed508584250
4
- data.tar.gz: 689d36f2f3b71bcc6192129514a8ca14b2b32001
3
+ metadata.gz: 02bcfb726015c3bfd566bb7ac4fa2ec7827f9916
4
+ data.tar.gz: 41febde66849c34ad23f4d031b0ca4cdd91b26fd
5
5
  SHA512:
6
- metadata.gz: c3c067aab6e1418a829a72505ad33f5822d3f7c749ece67aa903c306e7910ab2158cf5005f439d3dceffe032394f321109fc618c892b4324e85a2a469c8de6eb
7
- data.tar.gz: 9500d35aedd9849fd2893534b6a220256ac71696b973e86978b4d714233a17a31acd017efdd55cd0dee18afdd25522ab05a0f357e2023819784067fecce687b4
6
+ metadata.gz: 04113508f25059be7d25c184acedd3dfe20b9d2f2e267c5d7e5a62ee658f5049f11650da4d8a8565c4b1fccf9084b700ea6477fef32a3a36cf6b87face2136f8
7
+ data.tar.gz: 3c3bf38b691b39a7fd8f3a510738a9dbe8662796ccf509b20666f2d422f26452c70159e3291303eac44a9db62f142bba8ef08dd4e2cee1f65db6bf4184362f36
data/README.md CHANGED
@@ -19,7 +19,7 @@ Failure(1).to_s # => "1"
19
19
  Failure(Failure(1)) # => Failure(1)
20
20
  ```
21
21
 
22
- #### `fmap :: R a -> (a -> b) -> R b`
22
+ #### `fmap(self: Result(a), op: |a| -> b) -> Result(b)`
23
23
 
24
24
  Maps a `Result` with the value `a` to the same `Result` with the value `b`.
25
25
 
@@ -28,7 +28,7 @@ Success(1).fmap { |v| v + 1} # => Success(2)
28
28
  Failure(1).fmap { |v| v - 1} # => Failure(0)
29
29
  ```
30
30
 
31
- #### `bind :: R a -> (a -> R b) -> R b`
31
+ #### `bind(self: Result(a), op: |a| -> Result(b)) -> Result(b)`
32
32
 
33
33
  Maps a `Result` with the value `a` to another `Result` with the value `b`.
34
34
 
@@ -37,7 +37,7 @@ Success(1).bind { |v| Failure(v + 1) } # => Failure(2)
37
37
  Failure(1).fmap { |v| Success(v - 1) } # => Success(0)
38
38
  ```
39
39
 
40
- #### `map :: S a -> (a -> R b) -> R b`
40
+ #### `map(self: Success(a), op: |a| -> Result(b)) -> Result(b)`
41
41
 
42
42
  Maps a `Success` with the value `a` to another `Result` with the value `b`. It works like `#bind` but only on `Success`.
43
43
 
@@ -46,7 +46,7 @@ Success(1).map { |n| n + 1 } # => Success(2)
46
46
  Failure(0).map { |n| n + 1 } # => Failure(0)
47
47
  ```
48
48
 
49
- #### `map_err :: F a -> (a -> R b) -> R b`
49
+ #### `map_err(self: Failure(a), op: |a| -> Result(b)) -> Result(b)`
50
50
 
51
51
  Maps a `Failure` with the value `a` to another `Result` with the value `b`. It works like `#bind` but only on `Failure`.
52
52
 
@@ -55,7 +55,7 @@ Failure(1).map_err { |n| n + 1 } # => Success(2)
55
55
  Success(0).map_err { |n| n + 1 } # => Success(0)
56
56
  ```
57
57
 
58
- #### `try :: S a -> ( a -> R b) -> R b`
58
+ #### `try(self: Success(a), op: |a| -> Result(b)) -> Result(b)`
59
59
 
60
60
  Just like `#map`, transforms `a` to another `Result`, but it will also catch raised exceptions and wrap them with a `Failure`.
61
61
 
@@ -63,7 +63,7 @@ Just like `#map`, transforms `a` to another `Result`, but it will also catch rai
63
63
  Success(0).try { |n| raise "Error" } # => Failure(Error)
64
64
  ```
65
65
 
66
- #### `and :: S a -> R b -> R b`
66
+ #### `and(self: Success(a), other: Result(b)) -> Result(b)`
67
67
 
68
68
  Replaces `Success a` with `Result b`. If a `Failure` is passed as argument, it is ignored.
69
69
 
@@ -72,7 +72,7 @@ Success(1).and Success(2) # => Success(2)
72
72
  Failure(1).and Success(2) # => Failure(1)
73
73
  ```
74
74
 
75
- #### `and_then :: S a -> (a -> R b) -> R b`
75
+ #### `and_then(self: Success(a), op: |a| -> Result(b)) -> Result(b)`
76
76
 
77
77
  Replaces `Success a` with the result of the block. If a `Failure` is passed as argument, it is ignored.
78
78
 
@@ -81,7 +81,7 @@ Success(1).and_then { Success(2) } # => Success(2)
81
81
  Failure(1).and_then { Success(2) } # => Failure(1)
82
82
  ```
83
83
 
84
- #### `or :: F a -> R b -> R b`
84
+ #### `or(self: Failure(a), other: Result(b)) -> Result(b)`
85
85
  Replaces `Failure a` with `Result`. If a `Failure` is passed as argument, it is ignored.
86
86
 
87
87
  ```ruby
@@ -89,7 +89,7 @@ Success(1).or Success(2) # => Success(1)
89
89
  Failure(1).or Success(1) # => Success(1)
90
90
  ```
91
91
 
92
- #### `or_else :: F a -> (a -> R b) -> R b`
92
+ #### `or_else(self: Failure(a), op: |a| -> Result(b)) -> Result(b)`
93
93
 
94
94
  Replaces `Failure a` with the result of the block. If a `Success` is passed as argument, it is ignored.
95
95
 
@@ -98,7 +98,7 @@ Success(1).or_else { Success(2) } # => Success(1)
98
98
  Failure(1).or_else { |n| Success(n)} # => Success(1)
99
99
  ```
100
100
 
101
- #### `pipe :: R a -> (R a -> b) -> R a`
101
+ #### `pipe(self: Result(a), op: |Result(a)| -> b) -> Result(a)`
102
102
 
103
103
  Executes the block passed, but completely ignores its result. If an error is raised within the block it will **NOT** be catched.
104
104
 
@@ -269,16 +269,16 @@ Failure(1).result? # => true
269
269
 
270
270
 
271
271
  ## Maybe
272
- The simplest NullObject wrapper there can be. It adds `#some?` and `#none?` to `Object` though.
272
+ The simplest NullObject wrapper there can be. It adds `#some?` and `#null?` to `Object` though.
273
273
 
274
274
  ```ruby
275
275
  require 'deterministic/maybe' # you need to do this explicitly
276
- Maybe(nil).foo # => None
277
- Maybe(nil).foo.bar # => None
276
+ Maybe(nil).foo # => Null
277
+ Maybe(nil).foo.bar # => Null
278
278
  Maybe({a: 1})[:a] # => 1
279
279
 
280
- Maybe(nil).none? # => true
281
- Maybe({}).none? # => false
280
+ Maybe(nil).null? # => true
281
+ Maybe({}).null? # => false
282
282
 
283
283
  Maybe(nil).some? # => false
284
284
  Maybe({}).some? # => true
@@ -293,9 +293,9 @@ class Mimick
293
293
  def test; end
294
294
  end
295
295
 
296
- null = Maybe.mimick(Mimick)
297
- null.test # => None
298
- null.foo # => NoMethodError
296
+ naught = Maybe.mimick(Mimick)
297
+ naught.test # => Null
298
+ naught.foo # => NoMethodError
299
299
  ```
300
300
 
301
301
  ## Inspirations
data/lib/deterministic.rb CHANGED
@@ -5,9 +5,8 @@ warn "WARN: Deterministic is meant to run on Ruby 2+" if RUBY_VERSION.to_f < 2
5
5
  module Deterministic; end
6
6
 
7
7
  require 'deterministic/monad'
8
- require 'deterministic/result/match'
8
+ require 'deterministic/match'
9
9
  require 'deterministic/result/chain'
10
10
  require 'deterministic/result'
11
- require 'deterministic/result/success'
12
- require 'deterministic/result/failure'
13
- require 'deterministic/none'
11
+ require 'deterministic/option'
12
+ require 'deterministic/null'
@@ -4,11 +4,11 @@ module Deterministic
4
4
  module CoreExt
5
5
  module Result
6
6
  def success?
7
- self.is_a? Success
7
+ self.is_a? Deterministic::Result::Success
8
8
  end
9
9
 
10
10
  def failure?
11
- self.is_a? Failure
11
+ self.is_a? Deterministic::Result::Failure
12
12
  end
13
13
 
14
14
  def result?
@@ -0,0 +1,60 @@
1
+ module Deterministic
2
+ module PatternMatching
3
+
4
+ def match(context=nil, &block)
5
+ context ||= block.binding.eval('self') # the instance containing the match block
6
+ match = binding.eval('self.class::Match.new(self, context)') # the class defining the Match
7
+ match.instance_eval &block
8
+ match.call
9
+ end
10
+
11
+ class NoMatchError < StandardError; end
12
+
13
+ module Match
14
+ def initialize(container, context)
15
+ @container = container
16
+ @context = context
17
+ @collection = []
18
+ end
19
+
20
+ def call
21
+ matcher = @collection.detect { |m| m.matches?(@container.value) }
22
+ raise NoMatchError, "No match could be made for #{@container.inspect}" if matcher.nil?
23
+ @context.instance_exec(@container.value, &matcher.block)
24
+ end
25
+
26
+ # catch-all
27
+ def any(value=nil, &result_block)
28
+ push(Object, value, result_block)
29
+ end
30
+
31
+ private
32
+ Matcher = Struct.new(:condition, :block) do
33
+ def matches?(value)
34
+ condition.call(value)
35
+ end
36
+ end
37
+
38
+ def push(type, condition, result_block)
39
+ condition_pred = case
40
+ when condition.nil?; ->(v) { true }
41
+ when condition.is_a?(Proc); condition
42
+ when condition.is_a?(Class); ->(v) { condition === @container.value }
43
+ else ->(v) { @container.value == condition }
44
+ end
45
+
46
+ matcher_pred = compose_predicates(type_pred[type], condition_pred)
47
+ @collection << Matcher.new(matcher_pred, result_block)
48
+ end
49
+
50
+ def compose_predicates(f, g)
51
+ ->(*args) { f[*args] && g[*args] }
52
+ end
53
+
54
+ # return a partial function for matching a matcher's type
55
+ def type_pred
56
+ (->(type, x) { @container.is_a? type }).curry
57
+ end
58
+ end
59
+ end
60
+ end
@@ -1,5 +1,5 @@
1
1
  class Object
2
- def none?
2
+ def null?
3
3
  false
4
4
  end
5
5
 
@@ -9,5 +9,5 @@ class Object
9
9
  end
10
10
 
11
11
  def Maybe(obj)
12
- obj.nil? ? None.instance : obj
12
+ obj.nil? ? Null.instance : obj
13
13
  end
@@ -25,8 +25,9 @@ module Deterministic
25
25
  # bind :: (a -> Mb) -> M a -> M b
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
- (proc || block).call(value, self.class).tap do |result|
29
- raise NotMonadError, "Expected #{result.inspect} to be an Result" unless result.is_a? self.class
28
+ (proc || block).call(value).tap do |result|
29
+ parent = self.class.superclass === Object ? self.class : self.class.superclass
30
+ raise NotMonadError, "Expected #{result.inspect} to be an #{parent}" unless result.is_a? parent
30
31
  end
31
32
  end
32
33
  alias :'>>=' :bind
@@ -37,6 +38,15 @@ module Deterministic
37
38
  @value
38
39
  end
39
40
 
41
+ def to_s
42
+ value.to_s
43
+ end
44
+
45
+ def inspect
46
+ name = self.class.name.split("::")[-1]
47
+ "#{name}(#{value})"
48
+ end
49
+
40
50
  # Two monads are equivalent if they are of the same type and when their values are equal
41
51
  def ==(other)
42
52
  return false unless other.is_a? self.class
@@ -44,7 +54,7 @@ module Deterministic
44
54
  end
45
55
 
46
56
  # Return the string representation of the Monad
47
- def to_s
57
+ def inspect
48
58
  pretty_class_name = self.class.name.split('::')[-1]
49
59
  "#{pretty_class_name}(#{self.value.inspect})"
50
60
  end
@@ -1,11 +1,11 @@
1
1
  # The simplest NullObject there can be
2
- class None
2
+ class Null
3
3
  class << self
4
4
  def method_missing(m, *args)
5
5
  if m == :new
6
6
  super
7
7
  else
8
- None.instance
8
+ Null.instance
9
9
  end
10
10
  end
11
11
 
@@ -13,7 +13,7 @@ class None
13
13
  @instance ||= new([])
14
14
  end
15
15
 
16
- def none?
16
+ def null?
17
17
  true
18
18
  end
19
19
 
@@ -26,7 +26,7 @@ class None
26
26
  end
27
27
 
28
28
  def ==(other)
29
- other.respond_to?(:none?) && other.none?
29
+ other.respond_to?(:null?) && other.null?
30
30
  end
31
31
  end
32
32
  private_class_method :new
@@ -49,7 +49,7 @@ class None
49
49
  super
50
50
  end
51
51
 
52
- def none?
52
+ def null?
53
53
  true
54
54
  end
55
55
 
@@ -63,10 +63,10 @@ class None
63
63
  end
64
64
 
65
65
  def inspect
66
- 'None'
66
+ 'Null'
67
67
  end
68
68
 
69
69
  def ==(other)
70
- other.respond_to?(:none?) && other.none?
70
+ other.respond_to?(:null?) && other.null?
71
71
  end
72
72
  end
@@ -0,0 +1,95 @@
1
+ module Deterministic
2
+ # Abstract parent of Some and None
3
+ class Option
4
+ include Monad
5
+
6
+ module PatternMatching
7
+ include Deterministic::PatternMatching
8
+ class Match
9
+ include Deterministic::PatternMatching::Match
10
+
11
+ %w[Some None Option].each do |s|
12
+ define_method s.downcase.to_sym do |value=nil, &block|
13
+ klas = Module.const_get("Deterministic::Option::#{s}")
14
+ push(klas, value, block)
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ include PatternMatching
21
+
22
+ # This is an abstract class, can't ever instantiate it directly
23
+ class << self
24
+ protected :new
25
+
26
+ def some?(expr)
27
+ to_option(expr) { expr.nil? }
28
+ end
29
+
30
+ def any?(expr)
31
+ to_option(expr) { expr.nil? || not(expr.respond_to?(:empty?)) || expr.empty? }
32
+ end
33
+
34
+ def to_option(expr, &predicate)
35
+ predicate.call(expr) ? None.new : Some.new(expr)
36
+ end
37
+
38
+ def try!
39
+ yield rescue None.new
40
+ end
41
+ end
42
+
43
+ def map(proc=nil, &block)
44
+ return self if none?
45
+ bind(proc || block)
46
+ end
47
+
48
+ def some?
49
+ is_a? Some
50
+ end
51
+
52
+ def none?
53
+ is_a? None
54
+ end
55
+
56
+ class Some < Option
57
+ class << self; public :new; end
58
+ end
59
+
60
+ class None < Option
61
+ class << self; public :new; end
62
+ def initialize(*args); end
63
+
64
+ def inspect
65
+ "None"
66
+ end
67
+
68
+ # def value
69
+ # self
70
+ # end
71
+ def none(*args)
72
+ self
73
+ end
74
+
75
+ alias :fmap :none
76
+ alias :map :none
77
+
78
+ def ==(other)
79
+ other.class == self.class
80
+ end
81
+
82
+ # def value
83
+ # self # raise "value called on a None"
84
+ # end
85
+ end
86
+ end
87
+
88
+ module_function
89
+ def Some(value)
90
+ Option::Some.new(value)
91
+ end
92
+
93
+ None = Deterministic::Option::None.new
94
+ end
95
+ # p Deterministic::Option::Some::Match.new(.methods
@@ -2,15 +2,24 @@ module Deterministic
2
2
  # Abstract parent of Success and Failure
3
3
  class Result
4
4
  include Monad
5
- include Deterministic::PatternMatching
6
- include Chain
7
5
 
8
- def bind(proc=nil, &block)
9
- (proc || block).call(value).tap do |result|
10
- raise NotMonadError, "Expected #{result.inspect} to be an Result" unless result.is_a? self.class.superclass
6
+ module PatternMatching
7
+ include Deterministic::PatternMatching
8
+ class Match
9
+ include Deterministic::PatternMatching::Match
10
+
11
+ %w[Success Failure Result].each do |s|
12
+ define_method s.downcase.to_sym do |value=nil, &block|
13
+ klas = Module.const_get("Deterministic::Result::#{s}")
14
+ push(klas, value, block)
15
+ end
16
+ end
11
17
  end
12
18
  end
13
19
 
20
+ include PatternMatching
21
+ include Chain
22
+
14
23
  def success?
15
24
  is_a? Success
16
25
  end
@@ -52,23 +61,22 @@ module Deterministic
52
61
  class << self
53
62
  protected :new
54
63
  end
55
-
56
- def to_s
57
- value.to_s
64
+
65
+ class Failure < Result
66
+ class << self; public :new; end
58
67
  end
59
-
60
- def inspect
61
- name = self.class.name.split("::")[-1]
62
- "#{name}(#{value})"
68
+
69
+ class Success < Result
70
+ class << self; public :new; end
63
71
  end
64
72
  end
65
73
 
66
74
  module_function
67
75
  def Success(value)
68
- Success.new(value)
76
+ Result::Success.new(value)
69
77
  end
70
78
 
71
79
  def Failure(value)
72
- Failure.new(value)
80
+ Result::Failure.new(value)
73
81
  end
74
82
  end
@@ -1,3 +1,3 @@
1
1
  module Deterministic
2
- VERSION = "0.10.0"
2
+ VERSION = "0.12.0"
3
3
  end
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+
3
+ include Deterministic
4
+
5
+ class ElasticSearchConfig
6
+ def initialize(env="development", proc_env=ENV)
7
+ @env, @proc_env = env, proc_env
8
+ end
9
+
10
+ attr_reader :env
11
+
12
+ def hosts
13
+ Option.any?(proc_env["RESFINITY_LOG_CLIENT_ES_HOST"]).match {
14
+ some { |s| { hosts: s.split(/, */) } }
15
+ none { default_hosts }
16
+ }
17
+ end
18
+
19
+ private
20
+ attr_reader :proc_env
21
+ def default_hosts
22
+ case env
23
+ when "production"
24
+ { hosts: ["resfinity.net:9200"] }
25
+ when "acceptance" || "development"
26
+ { hosts: ["acc.resfinity.net:9200"] }
27
+ else
28
+ { hosts: ["localhost:9200"] }
29
+ end
30
+ end
31
+ end
32
+
33
+ describe ElasticSearchConfig do
34
+ let(:cfg) { ElasticSearchConfig.new(environment, env) }
35
+ context "test" do
36
+ let(:environment) { "test" }
37
+ context "env empty" do
38
+ let(:env) { {} }
39
+ specify { expect(cfg.hosts).to eq({ hosts: ["localhost:9200"] }) }
40
+ end
41
+
42
+ context "env empty" do
43
+ let(:env) { { "RESFINITY_LOG_CLIENT_ES_HOST" => "" } }
44
+ specify { expect(cfg.hosts).to eq({ hosts: ["localhost:9200"] }) }
45
+ end
46
+
47
+ context "env contains one" do
48
+ let(:env) { { "RESFINITY_LOG_CLIENT_ES_HOST" => "foo:9999"} }
49
+ specify { expect(cfg.hosts).to eq({ hosts: ["foo:9999"] }) }
50
+ end
51
+
52
+ context "env contains two" do
53
+ let(:env) { { "RESFINITY_LOG_CLIENT_ES_HOST" => "foo:9999,bar:9200"} }
54
+ specify { expect(cfg.hosts).to eq({ hosts: ["foo:9999", "bar:9200"] }) }
55
+ end
56
+ end
57
+
58
+ context "production" do
59
+ let(:environment) { "production" }
60
+ context "env empty" do
61
+ let(:env) { {} }
62
+ specify { expect(cfg.hosts).to eq({ hosts: ["resfinity.net:9200"] }) }
63
+ end
64
+ end
65
+
66
+ context "acceptance" do
67
+ let(:environment) { "acceptance" }
68
+ context "env empty" do
69
+ let(:env) { {} }
70
+ specify { expect(cfg.hosts).to eq({ hosts: ["acc.resfinity.net:9200"] }) }
71
+ end
72
+ end
73
+ end
@@ -19,13 +19,13 @@ describe ValidateAddress do
19
19
  subject { ValidateAddress.call(candidate) }
20
20
  context 'sunny day' do
21
21
  let(:candidate) { {title: "Hobbiton", street: "501 Buckland Rd", city: "Matamata", postal: "3472", country: "nz"} }
22
- specify { expect(subject).to be_a Success }
22
+ specify { expect(subject).to be_a Result::Success }
23
23
  specify { expect(subject.value).to eq candidate }
24
24
  end
25
25
 
26
26
  context 'empty data' do
27
27
  let(:candidate) { {} }
28
- specify { expect(subject).to be_a Failure }
28
+ specify { expect(subject).to be_a Result::Failure }
29
29
  specify { expect(subject.value).to include(:street, :city, :postal) }
30
30
  end
31
31
  end
@@ -3,16 +3,16 @@ require 'deterministic/maybe'
3
3
 
4
4
  describe 'maybe' do
5
5
  it "does something" do
6
- expect(Maybe(nil).foo).to be_none
7
- expect(Maybe(nil).foo.bar.baz).to be_none
8
- expect(Maybe(nil).fetch(:a)).to be_none
6
+ expect(Maybe(nil).foo).to be_null
7
+ expect(Maybe(nil).foo.bar.baz).to be_null
8
+ expect(Maybe(nil).fetch(:a)).to be_null
9
9
  expect(Maybe(1)).to be_some
10
10
  expect(Maybe({a: 1}).fetch(:a)).to eq 1
11
11
  expect(Maybe({a: 1})[:a]).to eq 1
12
12
  expect(Maybe("a").upcase).to eq "A"
13
- expect(Maybe("a")).not_to be_none
13
+ expect(Maybe("a")).not_to be_null
14
14
 
15
- # expect(Maybe[[]]).to eq be_none
15
+ # expect(Maybe[[]]).to eq be_null
16
16
  end
17
17
 
18
18
  end
@@ -7,12 +7,15 @@ describe Deterministic::Monad do
7
7
  include Deterministic::Monad
8
8
  end
9
9
 
10
+ let(:monad) { Identity }
10
11
  it_behaves_like 'a Monad' do
11
- let(:monad) { Identity }
12
+ # let(:monad) { monad }
12
13
  end
13
14
 
14
- specify { expect(Identity.new(1).to_s).to eq 'Identity(1)' }
15
- specify { expect(Identity.new(nil).to_s).to eq 'Identity(nil)' }
15
+ specify { expect(Identity.new(1).inspect).to eq 'Identity(1)' }
16
+ specify { expect(Identity.new(1).to_s).to eq '1' }
17
+ specify { expect(Identity.new(nil).inspect).to eq 'Identity(nil)' }
18
+ specify { expect(Identity.new(nil).to_s).to eq '' }
16
19
  specify { expect(Identity.new([1, 2]).fmap(&:to_s)).to eq Identity.new("[1, 2]") }
17
20
  specify { expect(Identity.new(1).fmap {|v| v + 2}).to eq Identity.new(3) }
18
21
  specify { expect(Identity.new('foo').fmap(&:upcase)).to eq Identity.new('FOO')}
@@ -25,24 +28,17 @@ describe Deterministic::Monad do
25
28
 
26
29
  it "passes the monad class, this is ruby-fu?!" do
27
30
  Identity.new(1)
28
- .bind do |_, monad|
31
+ .bind do |_|
29
32
  expect(monad).to eq Identity
30
33
  monad.new(_)
31
34
  end
32
35
  end
33
36
 
34
37
  specify { expect(
35
- Identity.new(1).bind { |value, monad| monad.new(value + 1) }
38
+ monad.new(1).bind { |value| monad.new(value + 1) }
36
39
  ).to eq Identity.new(2)
37
40
  }
38
41
 
39
42
  end
40
43
  specify { expect(Identity.new(Identity.new(1))).to eq Identity.new(1) }
41
-
42
- # it 'delegates #flat_map to an underlying collection and wraps the resulting collection' do
43
- # Identity.unit([1,2]).flat_map {|v| v + 1}.should == Identity.unit([2, 3])
44
- # Identity.unit(['foo', 'bar']).flat_map(&:upcase).should == Identity.unit(['FOO', 'BAR'])
45
- # expect { Identity.unit(1).flat_map {|v| v + 1 } }.to raise_error(RuntimeError)
46
- # end
47
-
48
44
  end
@@ -0,0 +1,49 @@
1
+ require "spec_helper"
2
+ require "deterministic/null"
3
+
4
+ describe Null do
5
+ it "Null is a Singleton" do
6
+ expect(Null.instance).to be_a Null
7
+ expect { Null.new }.to raise_error(NoMethodError, "private method `new' called for Null:Class")
8
+ end
9
+
10
+ it "explicit conversions" do
11
+ expect(Null.to_s).to eq 'Null'
12
+ expect(Null.inspect).to eq 'Null'
13
+ end
14
+
15
+ it "compares to Null" do
16
+ expect(Null === Null.instance).to be_truthy
17
+ expect(Null.instance === Null).to be_truthy
18
+ expect(Null.instance).to eq Null
19
+ expect(Null).to eq Null.instance
20
+ expect(1).not_to be Null
21
+ expect(1).not_to be Null.instance
22
+ expect(Null.instance).not_to be 1
23
+ expect(Null).not_to be 1
24
+ expect(Null.instance).not_to be_nil
25
+ expect(Null).not_to be_nil
26
+ end
27
+
28
+ it "implicit conversions" do
29
+ null = Null.instance
30
+ expect(null.to_str).to eq ""
31
+ expect(null.to_ary).to eq []
32
+ expect("" + null).to eq ""
33
+
34
+ a, b, c = null
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 Null for their public methods" do
41
+ class UnderMimickTest
42
+ def test; end
43
+ end
44
+
45
+ mimick = Null.mimic(UnderMimickTest)
46
+ expect(mimick.test).to be_null
47
+ expect { mimick.i_dont_exist}.to raise_error(NoMethodError)
48
+ end
49
+ end
@@ -0,0 +1,92 @@
1
+ require 'spec_helper'
2
+ require_relative 'monad_axioms'
3
+
4
+ include Deterministic
5
+
6
+ describe Deterministic::Option do
7
+ # nil?
8
+ specify { expect(described_class.some?(nil)).to eq None }
9
+ specify { expect(described_class.some?(1)).to be_some }
10
+ specify { expect(described_class.some?(1)).to eq Some(1) }
11
+
12
+ # any?
13
+ specify { expect(described_class.any?(nil)).to be_none }
14
+ specify { expect(described_class.any?("")).to be_none }
15
+ specify { expect(described_class.any?([])).to be_none }
16
+ specify { expect(described_class.any?({})).to be_none }
17
+ specify { expect(described_class.any?([1])).to eq Some([1]) }
18
+ specify { expect(described_class.any?({foo: 1})).to eq Some({foo: 1}) }
19
+
20
+ # try!
21
+ specify { expect(described_class.try! { raise "error" }).to be_none }
22
+ end
23
+
24
+ describe Deterministic::Option::Some do
25
+ it_behaves_like 'a Monad' do
26
+ let(:monad) { described_class }
27
+ end
28
+
29
+ specify { expect(described_class.new(0)).to be_a Option::Some }
30
+ specify { expect(described_class.new(0)).to eq Some(0) }
31
+ specify { expect(described_class.new(0).some?).to be_truthy }
32
+ specify { expect(described_class.new(0).none?).to be_falsey }
33
+ specify { expect(described_class.new(0).value).to eq 0 }
34
+
35
+ specify { expect(described_class.new(1).fmap { |n| n + 1}).to eq Some(2) }
36
+ specify { expect(described_class.new(1).map { |n| Some(n + 1)}).to eq Some(2) }
37
+ specify { expect(described_class.new(1).map { |n| None }).to eq None }
38
+
39
+ specify {
40
+ expect(
41
+ Some(0).match {
42
+ some(1) { |n| 99 }
43
+ some(0) { |n| n + 1 }
44
+ none(1) {}
45
+ }
46
+ ).to eq 1
47
+ }
48
+
49
+ specify {
50
+ expect(
51
+ Some(nil).match {
52
+ none { 0 }
53
+ some { 1 }
54
+ }
55
+ ).to eq 1
56
+ }
57
+
58
+ specify {
59
+ expect(
60
+ Some(1).match {
61
+ none { 0 }
62
+ some(Fixnum) { 1 }
63
+ }
64
+ ).to eq 1
65
+ }
66
+
67
+ specify {
68
+ expect(
69
+ None.match {
70
+ none { 0 }
71
+ some { 1 }
72
+ }
73
+ ).to eq 0
74
+ }
75
+
76
+ end
77
+
78
+ describe Deterministic::Option::None do
79
+ # it_behaves_like 'a Monad' do
80
+ # let(:monad) { described_class }
81
+ # end
82
+
83
+ specify { expect(described_class.new).to eq None }
84
+ specify { expect(described_class.new.some?).to be_falsey }
85
+ specify { expect(described_class.new.none?).to be_truthy }
86
+ # specify { expect { described_class.new.value }.to raise_error RuntimeError }
87
+
88
+ specify { expect(described_class.new.fmap { |n| n + 1}).to eq None }
89
+ specify { expect(described_class.new.map { |n| nil }).to eq None }
90
+ specify { expect(described_class.new).to eq Option::None.new }
91
+ specify { expect(described_class.new).to eq None }
92
+ end
@@ -95,7 +95,7 @@ describe Deterministic::Result do
95
95
  Success(self)
96
96
  end
97
97
 
98
- def to_s
98
+ def inspect
99
99
  "Step #{@step}"
100
100
  end
101
101
 
@@ -114,7 +114,7 @@ describe Deterministic::Result do
114
114
 
115
115
  it "works" do
116
116
  test = SelfContextUnderTest.new.call
117
- expect(test).to be_a Success
117
+ expect(test).to be_a described_class::Success
118
118
  expect(test.inspect).to eq "Success(Step 3)"
119
119
  end
120
120
  end
@@ -144,7 +144,7 @@ describe Deterministic::Result do
144
144
  end
145
145
 
146
146
  actual = Success(1) >= method(:error)
147
- expect(actual.inspect).to eq "Failure(error 1)"
147
+ expect(actual.inspect).to eq "Failure(#<RuntimeError: error 1>)"
148
148
  end
149
149
  end
150
150
  end
@@ -4,7 +4,7 @@ require_relative 'result_shared'
4
4
 
5
5
  include Deterministic
6
6
 
7
- describe Deterministic::Failure do
7
+ describe Deterministic::Result::Failure do
8
8
 
9
9
  it_behaves_like 'a Monad' do
10
10
  let(:monad) { described_class }
@@ -15,6 +15,8 @@ describe Deterministic::Failure do
15
15
  specify { expect(subject).to be_an_instance_of described_class }
16
16
  specify { expect(subject).to be_failure }
17
17
  specify { expect(subject).not_to be_success }
18
+ specify { expect(subject.success?).to be_falsey }
19
+ specify { expect(subject.failure?).to be_truthy }
18
20
 
19
21
  specify { expect(subject).to be_an_instance_of described_class }
20
22
  specify { expect(subject).to eq(described_class.new(1)) }
@@ -104,7 +104,7 @@ describe Deterministic::Result::Match do
104
104
 
105
105
  expect(
106
106
  Failure(error_hash).match do
107
- failure(:nothing) {|v| raise "We should not get to this point" }
107
+ failure(:null) {|v| raise "We should not get to this point" }
108
108
  failure(:needs_more_salt) do |error|
109
109
  "Successfully failed with #{error[:arbitrary]}!"
110
110
  end
@@ -4,7 +4,7 @@ require_relative 'result_shared'
4
4
 
5
5
  include Deterministic
6
6
 
7
- describe Deterministic::Success do
7
+ describe Deterministic::Result::Success do
8
8
 
9
9
  it_behaves_like 'a Monad' do
10
10
  let(:monad) { described_class }
@@ -15,6 +15,8 @@ describe Deterministic::Success do
15
15
  specify { expect(subject).to be_an_instance_of described_class }
16
16
  specify { expect(subject).to be_success }
17
17
  specify { expect(subject).not_to be_failure }
18
+ specify { expect(subject.success?).to be_truthy }
19
+ specify { expect(subject.failure?).to be_falsey }
18
20
 
19
21
  specify { expect(subject).to be_an_instance_of described_class }
20
22
  specify { expect(subject).to eq(described_class.new(1)) }
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.10.0
4
+ version: 0.12.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-08-25 00:00:00.000000000 Z
11
+ date: 2014-08-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -127,23 +127,24 @@ files:
127
127
  - lib/deterministic.rb
128
128
  - lib/deterministic/core_ext/object/result.rb
129
129
  - lib/deterministic/core_ext/result.rb
130
+ - lib/deterministic/match.rb
130
131
  - lib/deterministic/maybe.rb
131
132
  - lib/deterministic/monad.rb
132
- - lib/deterministic/none.rb
133
+ - lib/deterministic/null.rb
134
+ - lib/deterministic/option.rb
133
135
  - lib/deterministic/result.rb
134
136
  - lib/deterministic/result/chain.rb
135
- - lib/deterministic/result/failure.rb
136
- - lib/deterministic/result/match.rb
137
- - lib/deterministic/result/success.rb
138
137
  - lib/deterministic/version.rb
139
138
  - spec/examples/bookings_spec.rb
139
+ - spec/examples/config_spec.rb
140
140
  - spec/examples/validate_address_spec.rb
141
141
  - spec/lib/deterministic/core_ext/object/either_spec.rb
142
142
  - spec/lib/deterministic/core_ext/result_spec.rb
143
143
  - spec/lib/deterministic/maybe_spec.rb
144
144
  - spec/lib/deterministic/monad_axioms.rb
145
145
  - spec/lib/deterministic/monad_spec.rb
146
- - spec/lib/deterministic/none_spec.rb
146
+ - spec/lib/deterministic/null_spec.rb
147
+ - spec/lib/deterministic/option_spec.rb
147
148
  - spec/lib/deterministic/result/chain_spec.rb
148
149
  - spec/lib/deterministic/result/failure_spec.rb
149
150
  - spec/lib/deterministic/result/match_spec.rb
@@ -177,13 +178,15 @@ specification_version: 4
177
178
  summary: see above
178
179
  test_files:
179
180
  - spec/examples/bookings_spec.rb
181
+ - spec/examples/config_spec.rb
180
182
  - spec/examples/validate_address_spec.rb
181
183
  - spec/lib/deterministic/core_ext/object/either_spec.rb
182
184
  - spec/lib/deterministic/core_ext/result_spec.rb
183
185
  - spec/lib/deterministic/maybe_spec.rb
184
186
  - spec/lib/deterministic/monad_axioms.rb
185
187
  - spec/lib/deterministic/monad_spec.rb
186
- - spec/lib/deterministic/none_spec.rb
188
+ - spec/lib/deterministic/null_spec.rb
189
+ - spec/lib/deterministic/option_spec.rb
187
190
  - spec/lib/deterministic/result/chain_spec.rb
188
191
  - spec/lib/deterministic/result/failure_spec.rb
189
192
  - spec/lib/deterministic/result/match_spec.rb
@@ -1,5 +0,0 @@
1
- module Deterministic
2
- class Failure < Result
3
- class << self; public :new; end
4
- end
5
- end
@@ -1,66 +0,0 @@
1
- module Deterministic::PatternMatching
2
-
3
- def match(context=nil, &block)
4
- context ||= block.binding.eval('self')
5
- match = Match.new(self, context)
6
- match.instance_eval &block
7
- match.call
8
- end
9
-
10
- class NoMatchError < StandardError; end
11
-
12
- class Match
13
- def initialize(container, context)
14
- @container = container
15
- @context = context
16
- @collection = []
17
- end
18
-
19
- def call
20
- matcher = @collection.detect { |m| m.matches?(@container.value) }
21
- raise NoMatchError, "No match could be made for #{@container.inspect}" if matcher.nil?
22
- @context.instance_exec(@container.value, &matcher.block)
23
- end
24
-
25
- # TODO: Result specific DSL, will need to move it to Result, later on
26
- %w[Success Failure Result].each do |s|
27
- define_method s.downcase.to_sym do |value=nil, &block|
28
- klas = Module.const_get(s)
29
- push(klas, value, block)
30
- end
31
- end
32
-
33
- # catch-all
34
- def any(value=nil, &result_block)
35
- push(Object, value, result_block)
36
- end
37
-
38
- private
39
- Matcher = Struct.new(:condition, :block) do
40
- def matches?(value)
41
- condition.call(value)
42
- end
43
- end
44
-
45
- def push(type, condition, result_block)
46
- condition_pred = case
47
- when condition.nil?; ->(v) { true }
48
- when condition.is_a?(Proc); condition
49
- when condition.is_a?(Class); ->(v) { condition === @container.value }
50
- else ->(v) { @container.value == condition }
51
- end
52
-
53
- matcher_pred = compose_predicates(type_pred[type], condition_pred)
54
- @collection << Matcher.new(matcher_pred, result_block)
55
- end
56
-
57
- def compose_predicates(f, g)
58
- ->(*args) { f[*args] && g[*args] }
59
- end
60
-
61
- # return a partial function for matching a matcher's type
62
- def type_pred
63
- (->(type, x) { @container.is_a? type }).curry
64
- end
65
- end
66
- end
@@ -1,5 +0,0 @@
1
- module Deterministic
2
- class Success < Result
3
- class << self; public :new; end
4
- end
5
- end
@@ -1,49 +0,0 @@
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