wrapped 0.0.3 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +104 -76
- data/lib/wrapped/blank.rb +28 -4
- data/lib/wrapped/present.rb +29 -10
- data/lib/wrapped/version.rb +1 -1
- data/spec/spec_helper.rb +8 -0
- data/spec/wrapped_spec.rb +124 -44
- data/wrapped.gemspec +2 -2
- metadata +8 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ef031daae3bfd8056b4b709aa3a500ac7197e541
|
4
|
+
data.tar.gz: 70978101ccd2d40c521e4dffd6d9f6420a9edb71
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d601312592eedffda77e3fa31e8efebfdf226ae06bd4a428213b6acda443cabc414e1af1d7fe4cd239d8abbbd7336d91c25079c0c62fdd83b7eb33b3a11a5c79
|
7
|
+
data.tar.gz: 1a8d7a800f1c17a4a1fb0f546864cace441c86c8dbb04e6ab315e9afcf38e02884831487841d8f8c7e415322e8f0ce5b9554cf5b2108820a1cecfde93b3f39c5
|
data/README.md
CHANGED
@@ -6,6 +6,13 @@ your code to find bugs earlier and faster. It works like this: any time you
|
|
6
6
|
write a method that could produce nil, you instead write a method that produces
|
7
7
|
a wrapped value.
|
8
8
|
|
9
|
+
Warning: Experimental
|
10
|
+
---------------------
|
11
|
+
|
12
|
+
This library is in an early, experimental phase and may change suddenly and
|
13
|
+
drastically. Backwards-compatibility should not be expected between releases,
|
14
|
+
and the project may be cancelled at any time.
|
15
|
+
|
9
16
|
Example
|
10
17
|
-------
|
11
18
|
|
@@ -14,33 +21,39 @@ Here's an example along with how it can help with errors:
|
|
14
21
|
Say you have a collection of users along with a method for accessing the first
|
15
22
|
user:
|
16
23
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
24
|
+
```ruby
|
25
|
+
class UserCollection
|
26
|
+
def initialize(users)
|
27
|
+
@users = users
|
28
|
+
end
|
29
|
+
|
30
|
+
def first_user
|
31
|
+
@users.first
|
32
|
+
end
|
33
|
+
end
|
34
|
+
```
|
26
35
|
|
27
36
|
Now your friend uses your awesome UserCollection code:
|
28
37
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
38
|
+
```ruby
|
39
|
+
class FriendGroups
|
40
|
+
def initialize(user_collections)
|
41
|
+
@user_collections = user_collections
|
42
|
+
end
|
33
43
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
end
|
38
|
-
end
|
44
|
+
def first_names
|
45
|
+
@user_collections.map do |user_collection|
|
46
|
+
user_collection.first_user.first_name
|
39
47
|
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
```
|
40
51
|
|
41
52
|
And then she tries it:
|
42
53
|
|
43
|
-
|
54
|
+
```ruby
|
55
|
+
FriendGroups.new([UserCollection.new([])]).first_names
|
56
|
+
```
|
44
57
|
|
45
58
|
... and it promptly blows up:
|
46
59
|
|
@@ -59,15 +72,17 @@ Right.
|
|
59
72
|
Instead what you want to do is wrap it. Wrap that nil. Make the user know that
|
60
73
|
they have to consider the result.
|
61
74
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
75
|
+
```ruby
|
76
|
+
class UserCollection
|
77
|
+
def initialize(users)
|
78
|
+
@users = users
|
79
|
+
end
|
80
|
+
|
81
|
+
def first_user
|
82
|
+
@users.first.wrapped
|
83
|
+
end
|
84
|
+
end
|
85
|
+
```
|
71
86
|
|
72
87
|
Now in your documentation you explain that it produces a wrapped value. And
|
73
88
|
people who skip documentation and instead read source code will see that it is
|
@@ -76,17 +91,19 @@ wrapped.
|
|
76
91
|
So they unwrap it, because they must. They can't even get a happy path without
|
77
92
|
unwrapping it.
|
78
93
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
94
|
+
```ruby
|
95
|
+
class FriendGroups
|
96
|
+
def initialize(user_collections)
|
97
|
+
@user_collections = user_collections
|
98
|
+
end
|
83
99
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
end
|
88
|
-
end
|
100
|
+
def first_names
|
101
|
+
@user_collections.map do |user_collection|
|
102
|
+
user_collection.first_user.unwrap_or('') {|user| user.first_name }
|
89
103
|
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
```
|
90
107
|
|
91
108
|
Cool Stuff
|
92
109
|
----------
|
@@ -99,15 +116,19 @@ wrapped value. By wrapping it you've just made it more powerful!
|
|
99
116
|
|
100
117
|
For example:
|
101
118
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
119
|
+
```ruby
|
120
|
+
irb(main):054:0> 1.wrapped.inject(0) {|_, n| n+1}
|
121
|
+
=> 2
|
122
|
+
irb(main):055:0> nil.wrapped.inject(0) {|_, n| n+1}
|
123
|
+
=> 0
|
124
|
+
```
|
106
125
|
|
107
126
|
And then we have `flat_map`, which you can use to produce another wrapped object:
|
108
127
|
|
109
|
-
|
110
|
-
|
128
|
+
```ruby
|
129
|
+
irb> 1.wrapped.flat_map {|n| (n + 1).wrapped}.flat_map {|n| (n*2).wrapped}.unwrap
|
130
|
+
=> 4
|
131
|
+
```
|
111
132
|
|
112
133
|
Those same people who will exclaim things about functors will, at this point,
|
113
134
|
get giddy about monads. I mean, they're right, but they can relax. It's just a
|
@@ -116,44 +137,51 @@ monad.
|
|
116
137
|
Those people ("what do you mean, 'those people'?!") may prefer the `fmap`
|
117
138
|
method:
|
118
139
|
|
119
|
-
|
120
|
-
|
140
|
+
```ruby
|
141
|
+
irb> 1.wrapped.fmap {|n| n+1}.unwrap_or(0) {|n| n+4}
|
142
|
+
=> 6
|
143
|
+
```
|
121
144
|
|
122
145
|
Other Methods
|
123
146
|
-------------
|
124
147
|
|
125
148
|
Then we added some convenience methods to all of this. Here's a tour:
|
126
149
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
150
|
+
```ruby
|
151
|
+
irb> require 'wrapped'
|
152
|
+
=> true
|
153
|
+
irb> 1.wrapped.unwrap_or(-1)
|
154
|
+
=> 1
|
155
|
+
irb> nil.wrapped.unwrap_or(-1)
|
156
|
+
=> -1
|
157
|
+
irb> 1.wrapped.present {|n| p n }.blank { puts "nothing!" }
|
158
|
+
1
|
159
|
+
=> #<Present:0x7fc570aed0e8 @value=1>
|
160
|
+
irb> nil.wrapped.present {|n| p n }.blank { puts "nothing!" }
|
161
|
+
nothing!
|
162
|
+
=> #<Blank:0x7fc570ae21c0>
|
163
|
+
irb> 1.wrapped.unwrap
|
164
|
+
=> 1
|
165
|
+
irb> nil.wrapped.unwrap
|
166
|
+
IndexError: Blank has no value
|
167
|
+
from /home/mike/wrapped/lib/wrapped/types.rb:43:in `unwrap'
|
168
|
+
from (irb):7
|
169
|
+
```
|
170
|
+
|
171
|
+
```ruby
|
172
|
+
irb> 1.wrapped.present?
|
173
|
+
=> true
|
174
|
+
irb> nil.wrapped.present?
|
175
|
+
=> false
|
176
|
+
irb> nil.wrapped.blank?
|
177
|
+
=> true
|
178
|
+
irb> 1.wrapped.blank?
|
179
|
+
=> false
|
180
|
+
irb> 1.wrapped.unwrap_or(0) {|n| n * 100}
|
181
|
+
=> 100
|
182
|
+
irb> nil.wrapped.unwrap_or(0) {|n| n * 100}
|
183
|
+
=> 0
|
184
|
+
```
|
157
185
|
|
158
186
|
Inspiration
|
159
187
|
-----------
|
@@ -166,4 +194,4 @@ functor or the option class for more.
|
|
166
194
|
|
167
195
|
Copyright
|
168
196
|
---------
|
169
|
-
Copyright 2011
|
197
|
+
Copyright 2011 Mike Burns. Copyright 2014 thoughtbot.
|
data/lib/wrapped/blank.rb
CHANGED
@@ -10,8 +10,12 @@ class Blank
|
|
10
10
|
# Produce the value that is passed in.
|
11
11
|
#
|
12
12
|
# > w.unwrap_or(0)
|
13
|
-
def unwrap_or(default)
|
14
|
-
|
13
|
+
def unwrap_or(default = nil)
|
14
|
+
if block_given?
|
15
|
+
yield
|
16
|
+
else
|
17
|
+
default
|
18
|
+
end
|
15
19
|
end
|
16
20
|
|
17
21
|
# Does nothing, returning itself. This is chainable. See blank for its
|
@@ -37,7 +41,24 @@ class Blank
|
|
37
41
|
#
|
38
42
|
# > w.each {|n| puts n }
|
39
43
|
def each
|
40
|
-
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
# Produces itself.
|
48
|
+
def select
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
alias_method :find_all, :select
|
53
|
+
|
54
|
+
# Produces itself.
|
55
|
+
def reject
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
# Produces itself.
|
60
|
+
def grep(*)
|
61
|
+
self
|
41
62
|
end
|
42
63
|
|
43
64
|
# False; this is not an instance of a wrapped value.
|
@@ -64,12 +85,15 @@ class Blank
|
|
64
85
|
self
|
65
86
|
end
|
66
87
|
|
88
|
+
alias_method :collect, :fmap
|
89
|
+
alias_method :map, :fmap
|
90
|
+
|
67
91
|
# Is this wrapped value equal to the given wrapped value? All blank values
|
68
92
|
# are equal to each other.
|
69
93
|
#
|
70
94
|
# > nil.wrapped == nil.wrapped
|
71
95
|
# > 1.wrapped == nil.wrapped
|
72
96
|
def ==(other)
|
73
|
-
other.
|
97
|
+
other.is_a?(Blank)
|
74
98
|
end
|
75
99
|
end
|
data/lib/wrapped/present.rb
CHANGED
@@ -14,12 +14,8 @@ class Present
|
|
14
14
|
#
|
15
15
|
# > w.unwrap_or(0)
|
16
16
|
# > w.unwrap_or("hello") {|s| "Hi, #{s}" }
|
17
|
-
def unwrap_or(
|
18
|
-
|
19
|
-
yield unwrap
|
20
|
-
else
|
21
|
-
unwrap
|
22
|
-
end
|
17
|
+
def unwrap_or(_default = nil)
|
18
|
+
unwrap
|
23
19
|
end
|
24
20
|
|
25
21
|
# Invoke the block on the value, unwrapped. This method produces the wrapped
|
@@ -39,7 +35,7 @@ class Present
|
|
39
35
|
self
|
40
36
|
end
|
41
37
|
|
42
|
-
#
|
38
|
+
# Produces itself.
|
43
39
|
#
|
44
40
|
# If a block is passed, it is run against the unwrapped value.
|
45
41
|
#
|
@@ -48,7 +44,27 @@ class Present
|
|
48
44
|
# > w.each {|n| puts "Found #{n}" }
|
49
45
|
def each
|
50
46
|
yield unwrap if block_given?
|
51
|
-
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
# Produces itself if the block evaluates to true. Produces Blank if the block
|
51
|
+
# evaluates to false.
|
52
|
+
def select
|
53
|
+
super.first.wrapped
|
54
|
+
end
|
55
|
+
|
56
|
+
alias_method :find_all, :select
|
57
|
+
|
58
|
+
# Produces itself if the block evaluates to false. Produces Blank if the block
|
59
|
+
# evaluates to true.
|
60
|
+
def reject
|
61
|
+
super.first.wrapped
|
62
|
+
end
|
63
|
+
|
64
|
+
# Produces itself if the unwrapped value matches the given expression.
|
65
|
+
# Produces Blank otherwise.
|
66
|
+
def grep(*)
|
67
|
+
super.first.wrapped
|
52
68
|
end
|
53
69
|
|
54
70
|
# The raw value. I doubt you need this method.
|
@@ -84,14 +100,17 @@ class Present
|
|
84
100
|
#
|
85
101
|
# This makes it a functor.
|
86
102
|
def fmap
|
87
|
-
(yield unwrap)
|
103
|
+
Present.new(yield unwrap)
|
88
104
|
end
|
89
105
|
|
106
|
+
alias_method :collect, :fmap
|
107
|
+
alias_method :map, :fmap
|
108
|
+
|
90
109
|
# Is this wrapped value equal to the given wrapped value?
|
91
110
|
#
|
92
111
|
# > 1.wrapped == 1.wrapped
|
93
112
|
# > nil.wrapped == 2.wrapped
|
94
113
|
def ==(other)
|
95
|
-
unwrap == other.unwrap_or(nil)
|
114
|
+
other.is_a?(Present) && unwrap == other.unwrap_or(nil)
|
96
115
|
end
|
97
116
|
end
|
data/lib/wrapped/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
data/spec/wrapped_spec.rb
CHANGED
@@ -8,16 +8,16 @@ describe Wrapped, 'conversion' do
|
|
8
8
|
let(:delegator) { SimpleDelegator.new(value).wrapped }
|
9
9
|
|
10
10
|
it "converts the value to a Present" do
|
11
|
-
just.
|
11
|
+
expect(just).to be_instance_of(Present)
|
12
12
|
end
|
13
13
|
|
14
14
|
it "converts the nil to a Blank" do
|
15
|
-
nothing.
|
15
|
+
expect(nothing).to be_instance_of(Blank)
|
16
16
|
end
|
17
17
|
|
18
18
|
it "converts a simple delegator to a Present" do
|
19
|
-
delegator.
|
20
|
-
delegator.unwrap.
|
19
|
+
expect(delegator).to be_instance_of(Present)
|
20
|
+
expect(delegator.unwrap).to be_instance_of(SimpleDelegator)
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
@@ -27,7 +27,7 @@ describe Wrapped, 'accessing' do
|
|
27
27
|
let(:nothing) { nil.wrapped }
|
28
28
|
|
29
29
|
it 'produces the value of the wrapped object' do
|
30
|
-
just.unwrap.
|
30
|
+
expect(just.unwrap).to eq(value)
|
31
31
|
end
|
32
32
|
|
33
33
|
it 'raises an exception when called on the wrapped nil' do
|
@@ -43,44 +43,41 @@ describe Wrapped, 'callbacks' do
|
|
43
43
|
it 'calls the proper callback for a wrapped value' do
|
44
44
|
result = false
|
45
45
|
just.present {|v| result = v}
|
46
|
-
result.
|
46
|
+
expect(result).to be_truthy
|
47
47
|
end
|
48
48
|
|
49
49
|
it 'calls the proper callback for a wrapped nil' do
|
50
50
|
result = false
|
51
51
|
nothing.blank {result = true}
|
52
|
-
result.
|
52
|
+
expect(result).to be_truthy
|
53
53
|
end
|
54
54
|
|
55
55
|
it 'ignores the other callback for a wrapped value' do
|
56
56
|
result = true
|
57
57
|
just.blank { result = false }
|
58
|
-
result.
|
58
|
+
expect(result).to be_truthy
|
59
59
|
end
|
60
60
|
|
61
61
|
|
62
62
|
it 'ignores the other callback for a wrapped nil' do
|
63
63
|
result = true
|
64
64
|
nothing.present { result = false }
|
65
|
-
result.
|
65
|
+
expect(result).to be_truthy
|
66
66
|
end
|
67
67
|
|
68
68
|
it 'chains for wrapped values' do
|
69
69
|
result = false
|
70
70
|
just.present { result = true }.blank { result = false }
|
71
|
-
result.
|
71
|
+
expect(result).to be_truthy
|
72
72
|
end
|
73
73
|
|
74
74
|
it 'chains for wrapped nils' do
|
75
75
|
result = false
|
76
76
|
nothing.present { result = false }.blank { result = true }
|
77
|
-
result.
|
77
|
+
expect(result).to be_truthy
|
78
78
|
end
|
79
79
|
end
|
80
80
|
|
81
|
-
# This behavior is different from Haskell and Scala.
|
82
|
-
# It is done this way for consistency with Ruby.
|
83
|
-
# See the functor description later for `fmap'.
|
84
81
|
describe Wrapped, 'enumerable' do
|
85
82
|
let(:value) { 1 }
|
86
83
|
let(:just) { 1.wrapped }
|
@@ -89,29 +86,75 @@ describe Wrapped, 'enumerable' do
|
|
89
86
|
it 'acts over the value for #each on a wrapped value' do
|
90
87
|
result = -1
|
91
88
|
just.each {|v| result = v }
|
92
|
-
result.
|
89
|
+
expect(result).to eq(value)
|
93
90
|
end
|
94
91
|
|
95
|
-
it 'produces
|
96
|
-
just.each.
|
92
|
+
it 'produces itself for #each' do
|
93
|
+
expect(just.each).to eq(just)
|
97
94
|
end
|
98
95
|
|
99
96
|
it 'skips the block for #each on a wrapped nil' do
|
100
97
|
result = -1
|
101
98
|
nothing.each {|v| result = v }
|
102
|
-
result.
|
99
|
+
expect(result).to eq(-1)
|
103
100
|
end
|
104
101
|
|
105
|
-
it 'produces
|
106
|
-
nothing.each.
|
102
|
+
it 'produces blank for a wrapped nil on #each' do
|
103
|
+
expect(nothing.each).to eq(nothing)
|
107
104
|
end
|
108
105
|
|
109
106
|
it 'maps over the value for a wrapped value' do
|
110
|
-
just.map {|n| n + 1}.
|
107
|
+
expect(just.map {|n| n + 1}).to eq((value+1).wrapped)
|
111
108
|
end
|
112
109
|
|
113
|
-
it 'map produces
|
114
|
-
nothing.map {|n| n + 1}.
|
110
|
+
it 'map produces blank' do
|
111
|
+
expect(nothing.map {|n| n + 1}).to be_instance_of(Blank)
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'aliases map to collect' do
|
115
|
+
expect(just.method(:collect)).to be_alias_of(just.method(:map))
|
116
|
+
expect(nothing.method(:collect)).to be_alias_of(nothing.method(:map))
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'select produces present for a value matching the block' do
|
120
|
+
expect(just.select { |n| n == value }).to eq(just)
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'select produces blank for a value that does not match the block' do
|
124
|
+
expect(just.select { |n| n != value }).to be_instance_of(Blank)
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'select products blank for a blank' do
|
128
|
+
expect(nothing.select { true }).to be_instance_of(Blank)
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'aliases select to find_all' do
|
132
|
+
expect(just.method(:find_all)).to be_alias_of(just.method(:select))
|
133
|
+
expect(nothing.method(:find_all)).to be_alias_of(nothing.method(:select))
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'reject produces present for a value matching the block' do
|
137
|
+
expect(just.reject { |n| n != value }).to eq(just)
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'reject produces blank for a value that does not match the block' do
|
141
|
+
expect(just.reject { |n| n == value }).to be_instance_of(Blank)
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'reject products blank for a blank' do
|
145
|
+
expect(nothing.reject { true }).to be_instance_of(Blank)
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'grep produces present for a value matching the pattern' do
|
149
|
+
expect("hello".wrapped.grep(/ello$/)).to eq("hello".wrapped)
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'grep produces blank for a value that does not match the pattern' do
|
153
|
+
expect("hello".wrapped.grep(/^ello/)).to be_instance_of(Blank)
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'grep products blank for a blank' do
|
157
|
+
expect(nothing.grep(/.*/)).to be_instance_of(Blank)
|
115
158
|
end
|
116
159
|
end
|
117
160
|
|
@@ -121,13 +164,13 @@ describe Wrapped, 'queries' do
|
|
121
164
|
let(:nothing) { nil.wrapped }
|
122
165
|
|
123
166
|
it 'knows whether it is present' do
|
124
|
-
just.
|
125
|
-
nothing.
|
167
|
+
expect(just).to be_present
|
168
|
+
expect(nothing).not_to be_present
|
126
169
|
end
|
127
170
|
|
128
171
|
it 'knows whether it is blank' do
|
129
|
-
just.
|
130
|
-
nothing.
|
172
|
+
expect(just).not_to be_blank
|
173
|
+
expect(nothing).to be_blank
|
131
174
|
end
|
132
175
|
end
|
133
176
|
|
@@ -136,20 +179,20 @@ describe Wrapped, 'unwrap_or' do
|
|
136
179
|
let(:just) { 1.wrapped }
|
137
180
|
let(:nothing) { nil.wrapped }
|
138
181
|
|
139
|
-
it 'produces the value for a wrapped value' do
|
140
|
-
just.unwrap_or(-1).
|
182
|
+
it 'produces the value for a wrapped value with an argument' do
|
183
|
+
expect(just.unwrap_or(-1)).to eq(value)
|
141
184
|
end
|
142
185
|
|
143
|
-
it 'produces the
|
144
|
-
nothing.unwrap_or(-1).
|
186
|
+
it 'produces the argument for a wrapped nil with an argument' do
|
187
|
+
expect(nothing.unwrap_or(-1)).to eq(-1)
|
145
188
|
end
|
146
189
|
|
147
|
-
it 'produces the value
|
148
|
-
just.unwrap_or
|
190
|
+
it 'produces the value for a wrapped value with a block' do
|
191
|
+
expect(just.unwrap_or { value + 1 }).to eq(value)
|
149
192
|
end
|
150
193
|
|
151
|
-
it 'produces the
|
152
|
-
nothing.unwrap_or
|
194
|
+
it 'produces the block result for a wrapped nil with a block' do
|
195
|
+
expect(nothing.unwrap_or { 2 }).to eq(2)
|
153
196
|
end
|
154
197
|
end
|
155
198
|
|
@@ -159,11 +202,11 @@ describe Wrapped, 'monadic' do
|
|
159
202
|
let(:nothing) { nil.wrapped }
|
160
203
|
|
161
204
|
it 'produces the value from #flat_map for a wrapped value' do
|
162
|
-
just.flat_map {|n| (n+1).wrapped }.unwrap.
|
205
|
+
expect(just.flat_map {|n| (n+1).wrapped }.unwrap).to eq(value+1)
|
163
206
|
end
|
164
207
|
|
165
208
|
it 'produces blank from #flat_map for a wrapped nil' do
|
166
|
-
nothing.flat_map {|n| (n+1).wrapped}.
|
209
|
+
expect(nothing.flat_map {|n| (n+1).wrapped}).to be_blank
|
167
210
|
end
|
168
211
|
end
|
169
212
|
|
@@ -173,32 +216,69 @@ describe Wrapped, 'functor' do
|
|
173
216
|
let(:nothing) { nil.wrapped }
|
174
217
|
|
175
218
|
it 'unwraps, applies the block, then re-wraps for a wrapped value' do
|
176
|
-
just.fmap {|n| n+1}.unwrap.
|
219
|
+
expect(just.fmap {|n| n+1}.unwrap).to eq(value+1)
|
177
220
|
end
|
178
221
|
|
179
222
|
it 'produces the blank for a wrapped nil' do
|
180
|
-
nothing.fmap {|n| n+1}.
|
223
|
+
expect(nothing.fmap {|n| n+1}).to be_blank
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'obeys the functor law: fmap id == id' do
|
227
|
+
expect(fmap(id).(just)).to eq(id.(just))
|
228
|
+
end
|
229
|
+
|
230
|
+
it 'obeys the functor law: fmap (f . g) == fmap f . fmap g' do
|
231
|
+
expect(fmap(compose(null, const(nil))).(just)).
|
232
|
+
to eq(compose(fmap(null), fmap(const(nil))).(just))
|
233
|
+
end
|
234
|
+
|
235
|
+
def fmap(f)
|
236
|
+
lambda { |x| x.fmap(&f) }
|
237
|
+
end
|
238
|
+
|
239
|
+
def const(x)
|
240
|
+
lambda { |_| x }
|
241
|
+
end
|
242
|
+
|
243
|
+
def id
|
244
|
+
lambda { |x| x }
|
245
|
+
end
|
246
|
+
|
247
|
+
def compose(f, g)
|
248
|
+
lambda { |x| f.call(g.call(x)) }
|
249
|
+
end
|
250
|
+
|
251
|
+
def null
|
252
|
+
lambda {|x| x.nil? }
|
181
253
|
end
|
182
254
|
end
|
183
255
|
|
184
256
|
describe Wrapped, 'equality' do
|
185
257
|
it 'is equal with the same wrapped value' do
|
186
|
-
1.wrapped.
|
258
|
+
expect(1.wrapped).to eq(1.wrapped)
|
187
259
|
end
|
188
260
|
|
189
261
|
it 'is not equal with a different wrapped value' do
|
190
|
-
1.wrapped.
|
262
|
+
expect(1.wrapped).not_to eq(2.wrapped)
|
191
263
|
end
|
192
264
|
|
193
265
|
it 'is equal with two wrapped nils' do
|
194
|
-
nil.wrapped.
|
266
|
+
expect(nil.wrapped).to eq(nil.wrapped)
|
195
267
|
end
|
196
268
|
|
197
269
|
it 'is not equal with a wrapped nil and a wrapped value' do
|
198
|
-
nil.wrapped.
|
270
|
+
expect(nil.wrapped).not_to eq(1.wrapped)
|
199
271
|
end
|
200
272
|
|
201
273
|
it 'is not equal with a wrapped value and a wrapped nil' do
|
202
|
-
1.wrapped.
|
274
|
+
expect(1.wrapped).not_to eq(nil.wrapped)
|
275
|
+
end
|
276
|
+
|
277
|
+
it 'is not equal with a present value and un unwrapped value' do
|
278
|
+
expect(1.wrapped).not_to eq(1)
|
279
|
+
end
|
280
|
+
|
281
|
+
it 'is not equal with a blank value and an unwrapped value' do
|
282
|
+
expect(nil.wrapped).not_to eq(1)
|
203
283
|
end
|
204
284
|
end
|
data/wrapped.gemspec
CHANGED
@@ -16,6 +16,6 @@ Gem::Specification.new do |s|
|
|
16
16
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
17
|
s.require_paths = ["lib"]
|
18
18
|
|
19
|
-
s.add_development_dependency(
|
20
|
-
s.add_development_dependency(
|
19
|
+
s.add_development_dependency("rspec", "~> 3.1.0")
|
20
|
+
s.add_development_dependency("rake")
|
21
21
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wrapped
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Burns
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-11-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 3.1.0
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 3.1.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -83,8 +83,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
83
83
|
version: '0'
|
84
84
|
requirements: []
|
85
85
|
rubyforge_project:
|
86
|
-
rubygems_version: 2.
|
86
|
+
rubygems_version: 2.4.2
|
87
87
|
signing_key:
|
88
88
|
specification_version: 4
|
89
89
|
summary: The maybe functor for Ruby
|
90
|
-
test_files:
|
90
|
+
test_files:
|
91
|
+
- spec/spec_helper.rb
|
92
|
+
- spec/wrapped_spec.rb
|