monadic 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.0.4
4
+
5
+ To be more idiomatic rubyuesque, rename `#value` to `#fetch`, which throws now an `NoValueError`.
6
+ Thanks to [@pithyless](https://twitter.com/#!/pithyless) for the suggestion.
7
+
8
+ It now supports the Either monad, e.g.
9
+
10
+ either = Success(0).
11
+ bind { Success(1) }.
12
+ bind { Failure(2) }.
13
+ bind { Success(3) }
14
+
15
+ either == Failure(2) # the third bind is NOT executed
16
+
3
17
  ## v0.0.3
4
18
 
5
19
  `Some#map` and `Some#select` accept proc and block, you can now use:
@@ -9,3 +23,5 @@
9
23
  Option("FOO").downcase # old
10
24
 
11
25
  Removed `#none?`, please use `#empty?` instead.
26
+
27
+ `Some` and `None` are now in the `Monadic` namespace, however they are aliased when requiring `monadic`
data/README.md CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  helps dealing with exceptional situations, it comes from the sphere of functional programming and bringing the goodies I have come to love in Scala to my ruby projects (hence I will be using more Scala like constructs than Haskell).
4
4
 
5
- See also http://en.wikipedia.org/wiki/Monad
5
+ My motivation to create this gem was that I often work with nested Hashes and need to reach deeply inside of them so my code is sprinkled with things like some_hash.fetch(:one, {}).fetch(:two, {}).fetch(:three, "unknown").
6
6
 
7
7
  We have the following monadics:
8
8
 
9
- - Option (Maybe in Haskell)
10
- - Either *planned
9
+ - Option (Maybe in Haskell) - Scala like with a rubyesque flavour
10
+ - Either - more Haskell like
11
11
 
12
12
  What's the point of using monads in ruby? To me it started with having a safe way to deal with nil objects and other exceptions.
13
13
  Thus you contain the erroneous behaviour within a monad - an indivisible, impenetrable unit.
@@ -17,15 +17,14 @@ Thus you contain the erroneous behaviour within a monad - an indivisible, impene
17
17
  ### Option
18
18
  Is an optional type, which helps to handle error conditions gracefully. The one thing to remember about option is: 'What goes into the Option, stays in the Option'.
19
19
 
20
-
21
- Option(User.find(123)).name._ # ._ is a shortcut for .value
20
+ Option(User.find(123)).name._ # ._ is a shortcut for .fetch
22
21
 
23
22
  # if you prefer the alias Maybe instead of option
24
23
  Maybe(User.find(123)).name._
25
24
 
26
25
  # confidently diving into nested hashes
27
26
  Maybe({})[:a][:b][:c] == None
28
- Maybe({})[:a][:b][:c].value('unknown') == None
27
+ Maybe({})[:a][:b][:c].fetch('unknown') == None
29
28
  Maybe(a: 1)[:a]._ == 1
30
29
 
31
30
  Basic usage examples:
@@ -43,7 +42,7 @@ Basic usage examples:
43
42
 
44
43
  # Some stays Some, unless you unbox it
45
44
  Option('FOO').downcase == Some('foo')
46
- Option('FOO').downcase.value == "foo"
45
+ Option('FOO').downcase.fetch == "foo"
47
46
  Option('FOO').downcase._ == "foo"
48
47
  Option('foo').empty? == false
49
48
  Option('foo').truly? == true
@@ -68,7 +67,7 @@ Falsey values (kind-of) examples:
68
67
  user = Option(User.find(123))
69
68
  user.name._
70
69
 
71
- user.value('You are not logged in') { |user| "You are logged in as #{user.name}" }.should == 'You are logged in as foo'
70
+ user.fetch('You are not logged in') { |user| "You are logged in as #{user.name}" }.should == 'You are logged in as foo'
72
71
 
73
72
  if user != nil
74
73
  "You are logged in as foo"
@@ -77,7 +76,7 @@ Falsey values (kind-of) examples:
77
76
 
78
77
  user.subscribed? # always true
79
78
  user.subscribed?.truly? # true if subscribed is true
80
- user.subscribed?.value(false) # same as above
79
+ user.subscribed?.fetch(false) # same as above
81
80
  user.subscribed?.or(false) # same as above
82
81
 
83
82
  Remember! an Option is never false (in Ruby terms), if you want to know if it is false, call `#empty?` of `#truly?`
@@ -104,9 +103,65 @@ Slug example
104
103
  Option(title).strip.downcase.tr_s('^[a-z0-9]', '-')._('unknown-title')
105
104
  end
106
105
 
106
+ ### Either
107
+ Its main purpose here to handle errors gracefully, by chaining multiple calls in a functional way and stop evaluating them as soon as the first fails.
108
+ Assume you need several calls to construct some object in order to be useful, after each you need to check for success. Also you want to catch exceptions and not let them bubble upwards.
109
+
110
+ `Success` represents a successfull execution of an operation (Right in Scala, Haskell).
111
+ `Failure` represents a failure to execute an operation (Left in Scala, Haskell).
112
+
113
+ The `Either()` wrapper will treat `nil`, `false` or `empty?` as a `Failure` and all others as `Success`.
114
+
115
+ result = parse_and_validate_params(params).
116
+ bind ->(user_id) { User.find(user_id) }. # if #find returns null it will become a Failure
117
+ bind ->(user) { authorized?(user); user }. # if authorized? raises an Exception, it will be a Failure
118
+ bind ->(user) { UserDecorator(user) }
119
+
120
+ if result.success?
121
+ @user = result.fetch # result.fetch or result._ contains the
122
+ render 'page'
123
+ else
124
+ @error = result.fetch
125
+ render 'error_page'
126
+ end
127
+
128
+ You can use alternate syntaxes to achieve the same goal:
129
+
130
+ # block and Haskell like >= operator
131
+ Either(operation).
132
+ >= { successful_method }.
133
+ >= { failful_operation }
134
+
135
+ # start with a Success, for instance a parameter
136
+ Success('pzol').
137
+ bind ->(previous) { good }.
138
+ bind -> { bad }
139
+
140
+ Either.chain do
141
+ bind -> { good } # >= is not supported for Either.chain, only bind
142
+ bind -> { better } # better returns Success(some_int)
143
+ bind ->(previous_result) { previous_result + 1 }
144
+ end
145
+
146
+ either = Either(something)
147
+ either += truth? Success('truth, only the truth') : Failure('lies, damn lies')
148
+
149
+ Exceptions are wrapped into a Failure:
150
+
151
+ Either(true).
152
+ bind -> { fail 'get me out of here' } # return a Failure(RuntimeError)
153
+
154
+ Another example:
155
+
156
+ Success(params).
157
+ bind ->(params) { Either(params.fetch(:path)) }
158
+ bind ->(path) { load_stuff(params) }
159
+
107
160
 
108
- see also
161
+ ## References
109
162
 
163
+ * [Wikipedia Monad](See also http://en.wikipedia.org/wiki/Monad)
164
+ * [Learn You a Haskell - for a few monads more](http://learnyouahaskell.com/for-a-few-monads-more)
110
165
  * [Monad equivalend in Ruby](http://stackoverflow.com/questions/2709361/monad-equivalent-in-ruby)
111
166
  * [Option Type ](http://devblog.avdi.org/2011/05/30/null-objects-and-falsiness/)
112
167
  * [NullObject and Falsiness by @avdi](http://devblog.avdi.org/2011/05/30/null-objects-and-falsiness/)
@@ -0,0 +1,97 @@
1
+ # Chain various method calls
2
+ module Either
3
+ class NoEitherError < StandardError; end
4
+
5
+ def self.chain(initial=nil, &block)
6
+ Either::Chain.new(&block).call(initial)
7
+ end
8
+
9
+ def success?
10
+ is_a? Success
11
+ end
12
+
13
+ def failure?
14
+ is_a? Failure
15
+ end
16
+
17
+ def fetch(default=@value)
18
+ return default if failure?
19
+ return @value
20
+ end
21
+ alias :_ :fetch
22
+
23
+ def bind(proc=nil, &block)
24
+ return self if failure?
25
+
26
+ begin
27
+ result = if proc && proc.arity == 0
28
+ then proc.call
29
+ else (proc || block).call(@value)
30
+ end
31
+ result ||= Failure(nil)
32
+ result = Either(result) unless result.is_a? Either
33
+ result
34
+ rescue Exception => ex
35
+ Failure(ex)
36
+ end
37
+ end
38
+ alias :>= :bind
39
+ alias :+ :bind
40
+ alias :chain :bind
41
+
42
+ def to_s
43
+ "#{self.class.name}(#{@value.nil? ? 'nil' : @value.to_s})"
44
+ end
45
+
46
+ def ==(other)
47
+ return false unless self.class === other
48
+ return other.fetch == @value
49
+ end
50
+
51
+ end
52
+
53
+ class Either::Chain
54
+ def initialize(&block)
55
+ @chain = []
56
+ instance_eval(&block)
57
+ end
58
+
59
+ def call(initial)
60
+ @chain.inject(Success(initial)) do |result, current|
61
+ result.bind(current)
62
+ end
63
+ end
64
+
65
+ def bind(proc=nil, &block)
66
+ @chain << (proc || block)
67
+ end
68
+ alias :chain :bind
69
+
70
+ end
71
+
72
+ class Success
73
+ include Either
74
+ def initialize(value)
75
+ @value = value
76
+ end
77
+ end
78
+
79
+ class Failure
80
+ include Either
81
+ def initialize(value)
82
+ @value = value
83
+ end
84
+ end
85
+
86
+ def Success(value)
87
+ Success.new(value)
88
+ end
89
+
90
+ def Failure(value)
91
+ Failure.new(value)
92
+ end
93
+
94
+ def Either(value)
95
+ return Failure(value) if value.nil? || (value.respond_to?(:empty?) && value.empty?) || !value
96
+ return Success(value)
97
+ end
@@ -0,0 +1,4 @@
1
+ module Monadic
2
+ # thrown by Option#fetch if it has no value and no default was provided
3
+ class NoValueError < StandardError; end
4
+ end
@@ -5,89 +5,97 @@ require 'singleton'
5
5
  # Helper function which returns Some or None respectively, depending on their value
6
6
  # I find this moar simplistic in ruby than the traditional #bind and #unit
7
7
  def Option(value)
8
- return None if value.nil? || (value.respond_to?(:empty?) && value.empty?)
9
- return Some.new(value)
8
+ return Monadic::None if value.nil? || (value.respond_to?(:empty?) && value.empty?)
9
+ return Monadic::Some.new(value)
10
10
  end
11
11
  alias :Some :Option
12
12
  alias :Maybe :Option
13
13
 
14
- # Represents the Option if there is some value available
15
- class Some
16
- def initialize(value)
17
- @value = value
18
- end
19
-
20
- def to_ary
21
- return [@value].flatten if @value.respond_to? :flatten
22
- return [@value]
23
- end
24
- alias :to_a :to_ary
25
-
26
- def empty?
27
- false
28
- end
29
-
30
- def truly?
31
- @value == true
32
- end
33
-
34
- def value(default=None, &block)
35
- return default if empty?
36
- return block.call(@value) if block_given?
37
- return @value
38
- end
39
- alias :or :value
40
- alias :_ :value
41
14
 
42
- def map(func = nil, &block)
43
- return Option(@value.map(&block)) if @value.is_a?(Enumerable)
44
- return Option((func || block).call(@value))
45
- end
46
-
47
- def method_missing(m, *args)
48
- Option(@value.__send__(m, *args))
49
- end
50
-
51
- def select(func = nil, &block)
52
- return Option(@value.select(&block)) if @value.is_a?(Enumerable)
53
- return None unless (func || block).call(@value)
54
- return self
55
- end
56
-
57
- def ==(other)
58
- return false unless other.is_a? Some
59
- @value == other.instance_variable_get(:@value)
60
- end
61
-
62
- def to_s
63
- "Some(#{@value.to_s})"
64
- end
65
- end
15
+ # Represents the Option if there is some value available
16
+ module Monadic
17
+ class Some
18
+ def initialize(value)
19
+ @value = value
20
+ end
66
21
 
67
- # Represents the Option if there is no value available
68
- class None
69
- class << self
70
22
  def to_ary
71
- []
23
+ return [@value].flatten if @value.respond_to? :flatten
24
+ return [@value]
72
25
  end
73
26
  alias :to_a :to_ary
74
27
 
75
28
  def empty?
76
- true
29
+ false
30
+ end
31
+
32
+ def truly?
33
+ @value == true
34
+ end
35
+
36
+ def fetch(default=None, &block)
37
+ return block.call(@value) if block_given?
38
+ return @value
39
+ end
40
+ alias :or :fetch
41
+ alias :_ :fetch
42
+
43
+ def map(func = nil, &block)
44
+ return Option(@value.map(&block)) if @value.is_a?(Enumerable)
45
+ return Option((func || block).call(@value))
77
46
  end
78
47
 
79
48
  def method_missing(m, *args)
80
- self
49
+ Option(@value.__send__(m, *args))
81
50
  end
82
51
 
83
- def truly?
84
- false
52
+ def select(func = nil, &block)
53
+ return Option(@value.select(&block)) if @value.is_a?(Enumerable)
54
+ return None unless (func || block).call(@value)
55
+ return self
85
56
  end
86
57
 
87
- def value(default=self)
88
- default
58
+ def to_s
59
+ "Some(#{@value.to_s})"
60
+ end
61
+
62
+ def ==(other)
63
+ return false unless other.is_a? Some
64
+ @value == other.instance_variable_get(:@value)
65
+ end
66
+ end
67
+
68
+ # Represents the Option if there is no value available
69
+ class None
70
+ class << self
71
+ def to_ary
72
+ []
73
+ end
74
+ alias :to_a :to_ary
75
+
76
+ def empty?
77
+ true
78
+ end
79
+
80
+ def fetch(default=nil)
81
+ raise NoValueError if default.nil?
82
+ default
83
+ end
84
+ alias :or :fetch
85
+ alias :_ :fetch
86
+
87
+ def method_missing(m, *args)
88
+ self
89
+ end
90
+
91
+ def to_s
92
+ 'None'
93
+ end
94
+
95
+ def truly?
96
+ false
97
+ end
98
+
89
99
  end
90
- alias :or :value
91
- alias :_ :value
92
100
  end
93
101
  end
@@ -1,3 +1,3 @@
1
1
  module Monadic
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
data/lib/monadic.rb CHANGED
@@ -1,5 +1,12 @@
1
1
  require 'monadic/version'
2
2
 
3
- $LOAD_PATH << File.expand_path('..', __FILE__)
4
-
3
+ require 'monadic/errors'
5
4
  require 'monadic/option'
5
+ require 'monadic/either'
6
+
7
+ module Monadic
8
+ end
9
+
10
+ include Monadic
11
+ None = Monadic::None
12
+ Some = Monadic::Some
@@ -0,0 +1,194 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Either' do
4
+ it 'Success and Failure should be kind of Either' do
5
+ Success.new(0).should be_kind_of(Either)
6
+ Failure.new(0).should be_kind_of(Either)
7
+ end
8
+
9
+ it '#to_s works' do
10
+ Success.new("it worked!").to_s.should == "Success(it worked!)"
11
+ Failure.new(nil).to_s.should == "Failure(nil)"
12
+ end
13
+
14
+ it 'allows to verify equality' do
15
+ Success(1).should == Success(1)
16
+ Success(2).should_not == Success(1)
17
+ Success(1).should_not == Failure(1)
18
+ Failure(1).should == Failure(1)
19
+ Failure(1).should_not == Failure(2)
20
+ end
21
+
22
+ it 'wraps a nil result to Failure' do
23
+ Success(nil).bind { nil }.should == Failure(nil)
24
+ end
25
+
26
+ it 'catches exceptions and returns them as Failure' do
27
+ either = Success(nil).
28
+ bind { raise 'error' }
29
+
30
+ either.should be_kind_of Failure
31
+ error = either.fetch
32
+ error.should be_kind_of RuntimeError
33
+ error.message.should == 'error'
34
+ end
35
+
36
+ class User
37
+ def self.find(id)
38
+ case id
39
+ when -1; raise 'invalid user id'
40
+ when 0; nil
41
+ else User.new(id)
42
+ end
43
+ end
44
+
45
+ attr_reader :name
46
+ def initialize(id)
47
+ @name = "User #{id}"
48
+ end
49
+ end
50
+
51
+ it 'Either(nil || false) returns a Failure' do
52
+ Either(nil).should == Failure(nil)
53
+ Either(false).should == Failure(false)
54
+ Failure.should === Either(nil)
55
+ end
56
+
57
+ it 'Either with a non-falsey value returns success' do
58
+ Success.should === Either(1)
59
+ Success.should === Either(true)
60
+ Success.should === Either("string")
61
+ end
62
+
63
+ it 'works' do
64
+ either = Either(true).
65
+ chain -> { User.find(-1) }
66
+ either.failure?.should be_true
67
+ RuntimeError.should === either.fetch
68
+ end
69
+
70
+ it 'does NOT call subsequent binds on Failure and returns the first Failure in the end' do
71
+ either = Success(0).
72
+ bind { Failure(1) }.
73
+ bind { Failure(2) }.
74
+ bind { Success(3) }
75
+
76
+ either.should == Failure(1)
77
+ end
78
+
79
+ it 'returns the last Success' do
80
+ either = Success(nil).
81
+ bind { Success(1) }.
82
+ bind { Success(2) }
83
+
84
+ either.should == Success(2)
85
+ end
86
+
87
+ it 'allows to #fetch the value of the Success or Failure' do
88
+ Failure(1).fetch.should == 1
89
+ Success(2).fetch.should == 2
90
+ end
91
+
92
+ it 'allows to #fetch the value with a default if it failed' do
93
+ Failure(1).fetch(2).should == 2
94
+ Success(1).fetch(2).should == 1
95
+ end
96
+
97
+ it 'works with pattern matching (kind of)' do
98
+ either = Success('ok')
99
+
100
+ matched = case either
101
+ when Success; "yeah: #{either.fetch}"
102
+ when Failure; "oh no: #{either.fetch}"
103
+ end
104
+
105
+ matched.should == "yeah: ok"
106
+ end
107
+
108
+ it 'allows Haskell like syntax' do
109
+ either = Success(1).
110
+ >= { Success(2) }
111
+
112
+ either.should == Success(2)
113
+ end
114
+
115
+ it 'passes the result value from the previous call to the next' do
116
+ either = Success(1).
117
+ >= {|prev| Success(prev + 1) }. # a block
118
+ >= -> prev { Success(prev + 100) } # lambda/proc
119
+
120
+ either.should == Success(102)
121
+ end
122
+
123
+ it 'returns the boxed value with the #_ alias for #fetch' do
124
+ Failure(99)._.should == 99
125
+ end
126
+
127
+ it 'allows you to use parameterless lambdas (#arity == 0)' do
128
+ (Success(0) >=
129
+ -> { Success(1) }
130
+ ).should == Success(1)
131
+ end
132
+
133
+ it 'allows you to use lambdas with the + operator' do
134
+ either = Success(0) +
135
+ -> { Success(1) } +
136
+ -> { Success(2) }
137
+
138
+ either.should == Success(2)
139
+ end
140
+
141
+ it 'allows you to use +=' do
142
+ either = Success(0)
143
+ either += -> { Success(1) }
144
+
145
+ either.should == Success(1)
146
+ end
147
+
148
+ it 'allows to check whether the result was a success or failure' do
149
+ Success(0).success?.should be_true
150
+ Success(1).failure?.should be_false
151
+
152
+ Failure(2).success?.should be_false
153
+ Failure(3).failure?.should be_true
154
+ end
155
+
156
+ it 'supprts Either.chain' do
157
+ Either.chain do
158
+ bind -> { Success(1) }
159
+ bind -> { Success(2) }
160
+ end.should == Success(2)
161
+
162
+ Either.chain do
163
+ bind -> { Success(1) }
164
+ bind ->(p) { Success(p + 1) }
165
+ end.should == Success(2)
166
+
167
+ Either.chain do
168
+ bind { Success(1) }
169
+ end.should == Success(1)
170
+ end
171
+
172
+ it 'README example' do
173
+ params = { :path => 'foo' }
174
+ def load_file(path);
175
+ fail "invalid path" unless path
176
+ Success("bar");
177
+ end
178
+ def process_content(content); content.start_with?('b') ? Success(content.upcase) : Failure('invalid content'); end
179
+
180
+ either = Either(true).
181
+ bind { params.fetch(:path) }.
182
+ bind {|path| load_file(path) }.
183
+ bind {|content| process_content(content) }
184
+ either.should be_a_success
185
+ either.fetch.should == 'BAR'
186
+
187
+ either = Either(true).
188
+ bind { {}.fetch(:path) }.
189
+ bind {|path| load_file(path) }.
190
+ bind {|content| process_content(content) }
191
+ either.should be_a_failure
192
+ KeyError.should === either.fetch
193
+ end
194
+ end
data/spec/option_spec.rb CHANGED
@@ -6,7 +6,7 @@ describe 'Option' do
6
6
  end
7
7
 
8
8
  it 'None stays None' do
9
- Option(nil)._.should == None
9
+ expect { Option(nil)._ }.to raise_error Monadic::NoValueError
10
10
  Option(nil).empty?.should be_true
11
11
  end
12
12
 
@@ -22,7 +22,6 @@ describe 'Option' do
22
22
  it 'None#to_s is "None"' do
23
23
  option = Option(nil)
24
24
  "#{option}".should == "None"
25
- "#{option._}".should == "None"
26
25
  end
27
26
 
28
27
  it 'None is always empty' do
@@ -38,24 +37,24 @@ describe 'Option' do
38
37
  Option('FOO').downcase.should == Some('foo')
39
38
  end
40
39
 
41
- it 'returns the value of an option' do
42
- Option('foo').value.should == 'foo'
40
+ it '#fetch returns the value of an option' do
41
+ Option('foo').fetch.should == 'foo'
43
42
  Option('foo')._.should == 'foo'
44
43
  end
45
44
 
46
45
  it 'returns the value of an option with a default, in case value is None' do
47
- Option(nil).value('bar').should == 'bar'
46
+ Option(nil).fetch('bar').should == 'bar'
48
47
  Option(nil)._('bar').should == 'bar'
49
48
  end
50
49
 
51
50
  it 'returns the value and not the default if it is Some' do
52
- Option('FOO').downcase.value('bar').should == 'foo'
51
+ Option('FOO').downcase.fetch('bar').should == 'foo'
53
52
  Option('FOO').downcase._('bar').should == 'foo'
54
53
  end
55
54
 
56
55
  it 'returns the value applied to a block if it is Some' do
57
- Option('foo').value('bar') { |val| "You are logged in as #{val}" }.should == 'You are logged in as foo'
58
- Option(nil).value('You are not logged in') { |val| "You are logged in as #{val}" }.should == 'You are not logged in'
56
+ Option('foo').fetch('bar') { |val| "You are logged in as #{val}" }.should == 'You are logged in as foo'
57
+ Option(nil).fetch('You are not logged in') { |val| "You are logged in as #{val}" }.should == 'You are not logged in'
59
58
  end
60
59
 
61
60
  it 'is never falsey' do
@@ -73,9 +72,9 @@ describe 'Option' do
73
72
  end
74
73
  end
75
74
 
76
- it 'allows to use a block with value and _' do
75
+ it 'allows to use a block with fetch and _' do
77
76
  user = Option(User.new('foo'))
78
- user.value('You are not logged in') { |user| "You are logged in as #{user.name}" }.should == 'You are logged in as foo'
77
+ user.fetch('You are not logged in') { |user| "You are logged in as #{user.name}" }.should == 'You are logged in as foo'
79
78
  end
80
79
 
81
80
  it 'handles (kind-of) falsey values' do
@@ -120,12 +119,11 @@ describe 'Option' do
120
119
  select {|d| d.month == 3}. # filters out non-matching Date values (Some becomes None)
121
120
  map(&:to_s). # transforms a contained Date value into a String value
122
121
  map {|s| s.gsub('-', '')}. # transforms a contained String value by removing '-'
123
- value("not in march!") # returns the contained value, or the alternative if None
122
+ fetch("not in march!") # returns the contained value, or the alternative if None
124
123
  end
125
124
 
126
125
  format_date_in_march(nil).should == "not in march!"
127
126
  format_date_in_march(Time.parse('2009-01-01 01:02')).should == "not in march!"
128
127
  format_date_in_march(Time.parse('2011-03-21 12:34')).should == "20110321"
129
- end
130
-
128
+ end
131
129
  end
data/spec/spec_helper.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'bundler/setup'
2
2
  Bundler.require(:default, :test)
3
3
 
4
+ $LOAD_PATH << File.expand_path('../../lib', __FILE__)
4
5
  require File.expand_path('../../lib/monadic', __FILE__)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: monadic
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-30 00:00:00.000000000 Z
12
+ date: 2012-05-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
@@ -123,9 +123,12 @@ files:
123
123
  - README.md
124
124
  - Rakefile
125
125
  - lib/monadic.rb
126
+ - lib/monadic/either.rb
127
+ - lib/monadic/errors.rb
126
128
  - lib/monadic/option.rb
127
129
  - lib/monadic/version.rb
128
130
  - monadic.gemspec
131
+ - spec/either_spec.rb
129
132
  - spec/option_spec.rb
130
133
  - spec/spec_helper.rb
131
134
  homepage: http://github.com/pzol/monadic
@@ -153,5 +156,6 @@ signing_key:
153
156
  specification_version: 3
154
157
  summary: see README
155
158
  test_files:
159
+ - spec/either_spec.rb
156
160
  - spec/option_spec.rb
157
161
  - spec/spec_helper.rb