ryansch-andand 1.3.2
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +25 -0
- data/License.txt +26 -0
- data/README.textile +162 -0
- data/lib/andand.rb +148 -0
- data/lib/andand/version.rb +9 -0
- data/test/andand_spec.rb +133 -0
- data/test/test_helper.rb +2 -0
- metadata +76 -0
data/History.txt
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
== 1.3.2
|
2
|
+
|
3
|
+
* 1 minor enhancement
|
4
|
+
* better docs
|
5
|
+
* 2 major enhancement:
|
6
|
+
* Fix 1.9 warning (patch by George MacKerron)
|
7
|
+
* 3 major enhancement:
|
8
|
+
* Remove deprecated gemspec information (patch by Anthony Panozzo)
|
9
|
+
|
10
|
+
== 1.3.1 2008-07-12
|
11
|
+
|
12
|
+
* 1 major enhancement:
|
13
|
+
* Initial release
|
14
|
+
* 2 tiny enhancement:
|
15
|
+
* fixed specifications
|
16
|
+
* 3 minor enhancement
|
17
|
+
* tap is now a synonym for me
|
18
|
+
* 4 minor enhancement
|
19
|
+
* Object#dont
|
20
|
+
* 5 bug fix
|
21
|
+
* Plays well with other patch-artists
|
22
|
+
* 6 minor enhancement
|
23
|
+
* uses existing BlankSlate class if already defined
|
24
|
+
* 7 tiny enhancement
|
25
|
+
* use BasicObject as well
|
data/License.txt
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
Copyright (c) 2008 Reginald Braithwaite
|
2
|
+
http://weblog.raganwald.com/2008/01/objectandand-objectme-in-ruby.html
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person
|
5
|
+
obtaining a copy of this software and associated documentation
|
6
|
+
files (the "Software"), to deal in the Software without
|
7
|
+
restriction, including without limitation the rights to use,
|
8
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the
|
10
|
+
Software is furnished to do so, subject to the following
|
11
|
+
conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
18
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
20
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
21
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
22
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
23
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
24
|
+
|
25
|
+
Some code adapted from Jim Weirich's post:
|
26
|
+
http://onestepback.org/index.cgi/Tech/Ruby/BlankSlate.rdoc
|
data/README.textile
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
h1. Object#andand
|
2
|
+
|
3
|
+
h2. Breaking News
|
4
|
+
|
5
|
+
@Object#andand@'s functionality is also available as part of the "RewriteRails":http://github.com/raganwald/rewrite_rails plug-in. If you are using Ruby on Rails, please consider using RewriteRails instead of the andand gem.
|
6
|
+
|
7
|
+
h2. What
|
8
|
+
|
9
|
+
@Object#andand@ lets us write:
|
10
|
+
|
11
|
+
<pre syntax="ruby">
|
12
|
+
@phone = Location.find(:first, ...elided... ).andand.phone
|
13
|
+
</pre>And get a _guarded method invocation_ or _safe navigation method_. This snippet performs a @.find@ on the Location class, then sends @.phone@ to the result _if the result is not nil_. If the result is nil, then the expression returns nil without throwing a NoMethodError.
|
14
|
+
|
15
|
+
As Dejan Simic "put it":http://rors.org/2008/3/18/andand:
|
16
|
+
|
17
|
+
Why would you want to write this:
|
18
|
+
|
19
|
+
<pre syntax="ruby">
|
20
|
+
entry.at('description') && entry.at('description').inner_text
|
21
|
+
</pre>when you can write this:
|
22
|
+
|
23
|
+
<pre syntax="ruby">
|
24
|
+
entry.at('description').andand.inner_text
|
25
|
+
</pre>Why indeed! As a bonus, install andand and you will also receive an enhanced Object#tap method, _at no extra charge_!
|
26
|
+
|
27
|
+
h2. Installing
|
28
|
+
|
29
|
+
Update to RubyGems 1.2.0 before proceeding!!
|
30
|
+
|
31
|
+
<pre syntax="ruby">sudo gem sources -a http://gems.github.com (you only have to do this once)
|
32
|
+
sudo gem install raganwald-andand</pre>
|
33
|
+
|
34
|
+
Or:
|
35
|
+
|
36
|
+
<pre syntax="ruby">git clone git://github.com/raganwald/andand.git
|
37
|
+
cd andand
|
38
|
+
rake gem
|
39
|
+
rake install_gem</pre>
|
40
|
+
|
41
|
+
h2. The basics
|
42
|
+
|
43
|
+
h3. Object#andand
|
44
|
+
|
45
|
+
Ruby programmers are familiar with the two _guarded assignment_ operators @&&=@ and @||=@. The typical use for them is when you have a variable that might be nil. For example:
|
46
|
+
|
47
|
+
<pre syntax="ruby">
|
48
|
+
first_name &&= @first_name.trim
|
49
|
+
@phone ||= '612-777-9311'
|
50
|
+
</pre>You are trimming the first name provided it isn’t nil, and you are assigning ‘612-777-9311’ to the phone if it _is_ nil (or false, but that isn’t important right now). The other day we were discussing the guards and we agreed that we wished there was a _guarded method invocation_ operator. Here’s an example of when you would use it:
|
51
|
+
|
52
|
+
<pre syntax="ruby">
|
53
|
+
@phone = Location.find(:first, ...elided... )&&.phone
|
54
|
+
</pre>Meaning, search the location table for the first record matching some criteria, and if you find a location, get its phone. If you don’t, get nil. (Groovy provides this exact functionality, although Groovy uses @?.@ instead of @&&.@) However, @&&.@ won’t work because @&&.@ is not a real Ruby operator.
|
55
|
+
|
56
|
+
Object#andand let’s us write:
|
57
|
+
|
58
|
+
<pre syntax="ruby">
|
59
|
+
@phone = Location.find(:first, ...elided... ).andand.phone
|
60
|
+
</pre>And get the same effect as:
|
61
|
+
|
62
|
+
<pre syntax="ruby">
|
63
|
+
@phone = ->(loc){ loc && loc.phone }.call(Location.find(:first, ...elided... ))
|
64
|
+
</pre>Note that because you accept any method using Ruby’s method invocation syntax, you can accept methods with parameters and/or blocks:
|
65
|
+
|
66
|
+
<pre syntax="ruby">
|
67
|
+
list_of_lists.detect { ...elided... }.andand.inject(42) { ...elided ... }
|
68
|
+
</pre>Object#andand emphasizes syntactic regularity: the goal was to make an @&&.@ operation that worked like @&&=@. @&&=@ looks just like normal assignment, you can use any expression on the RHS, only the semantics are different. The andand method also works just like a normal method invocation, only the semantics are modified.
|
69
|
+
|
70
|
+
h3. Block-Structured andand
|
71
|
+
|
72
|
+
You can use @andand@ with a block instead of a method:
|
73
|
+
|
74
|
+
<pre syntax="ruby">
|
75
|
+
@phone = Location.find(:first, ...elided... ).andand { |location|
|
76
|
+
YellowPages.reverse_lookup(location).phone
|
77
|
+
}
|
78
|
+
</pre>If the receiver is nil, the block is not executed and @andand@ returns @nil@. If the receiver is not nil, it is passed as a parameter to the block and @andand@ returns the value of the block. This makes it possible to perform conditional evaluation and also to make the scope of the variable really obvious.
|
79
|
+
|
80
|
+
h3. Scope
|
81
|
+
|
82
|
+
@Object#andand@ only works for one method call. For example, @fu.andand.bar.blitz@ is going to call @nil.blitz@ if @fu@ is @nil@. That's because @fu.andand.bar@ is going to evaluate to @nil@, and then we will call the method @blitz@ on it. In most cases, you want to use @fu.andand.bar.andand.blitz@. If that seems awkward, you might want to reconsider violating the Law of Demeter in an environment where you can't be sure if your receiver is @nil@ or not.
|
83
|
+
|
84
|
+
Another example of this (pointed out by Jan Zimmek):
|
85
|
+
|
86
|
+
<pre syntax="ruby">
|
87
|
+
x = nil
|
88
|
+
x.andand.length > 3
|
89
|
+
</pre>This results in a @NoMethodError@. Again, @x@ is @nil@, therefore @x.andand.length@ is @nil@, therefore we end up with @nil > 3@ which results in a @NoMethodError@. This can be fixed with @x.andand.length.andand > 3@ as above, or perhaps:
|
90
|
+
|
91
|
+
<pre syntax="ruby">
|
92
|
+
x = nil
|
93
|
+
x.andand { |value| value.length > 3 }
|
94
|
+
</pre>
|
95
|
+
|
96
|
+
h3. Enhanced Object#tap
|
97
|
+
|
98
|
+
Ruby 1.9 introduces "Object#tap":http://moonbase.rydia.net/mental/blog/programming/eavesdropping-on-expressions. This library implements Object#tap for Ruby 1.8 *and* enhances it. As in Ruby 1.9, you can call @.tap@ with a block:
|
99
|
+
|
100
|
+
<pre syntax="ruby">
|
101
|
+
blah.sort.grep( /foo/ ).tap { |xs| p xs }.map { |x| x.blah }
|
102
|
+
</pre> But like its sibling @.andand@, you can now call @.tap@ with a method as well:
|
103
|
+
|
104
|
+
<pre syntax="ruby">
|
105
|
+
[1, 2, 3, 4, 5].tap.pop.map { |n| n * 2 }
|
106
|
+
=> [2, 4, 6, 8]
|
107
|
+
</pre>
|
108
|
+
|
109
|
+
h3. Doctor, it hurts when I do that
|
110
|
+
|
111
|
+
_So don't do that!_
|
112
|
+
|
113
|
+
The popular use case for Object#tap is poor man's debugging:
|
114
|
+
|
115
|
+
<pre syntax="ruby">
|
116
|
+
blah.sort.grep( /foo/ ).tap { |xs| p xs }.map { |x| x.blah }
|
117
|
+
</pre>Perhaps you want to remove the tap, you can delete it:
|
118
|
+
|
119
|
+
<pre syntax="ruby">
|
120
|
+
blah.sort.grep( /foo/ ).map { |x| x.blah }
|
121
|
+
</pre>Or, you can change it to @.dont@:
|
122
|
+
|
123
|
+
<pre syntax="ruby">
|
124
|
+
blah.sort.grep( /foo/ ).dont { |xs| p xs }.map { |x| x.blah }
|
125
|
+
</pre>Like @.andand@ and @.tap@, @.dont@ works with arbitrary methods, not just blocks:
|
126
|
+
|
127
|
+
<pre syntax="ruby">
|
128
|
+
(1..10).to_a.reverse!
|
129
|
+
=> [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
|
130
|
+
(1..10).to_a.dont.reverse!
|
131
|
+
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
132
|
+
</pre>
|
133
|
+
|
134
|
+
h2. A little more background
|
135
|
+
|
136
|
+
"Object#andand & Object#me in Ruby":http://weblog.raganwald.com/2008/01/objectandand-objectme-in-ruby.html explains the original motivations, as well as providing links to similar implementations you may want to consider. A few people have pointed out that Object#andand is similar to Haskell's Maybe monad. "The Maybe Monad in Ruby":http://blog.pretheory.com/arch/2008/02/the_maybe_monad_in_ruby.php is a good introduction for Ruby programmers.
|
137
|
+
|
138
|
+
h2. That's cool, but…
|
139
|
+
|
140
|
+
No problem, I get that andand isn't exactly what you need. Have a look at the "Invocation Construction Kit":http://ick.rubyforge.org or "Ick." The Ick gem _generalizes_ #andand and #tap: Ick provides four useful ways to block-structure your code, the methods #let, #returning, #inside, and #my. Ick also includes four quasi-monadic invocations, #maybe, #please, #tee, and #fork.
|
141
|
+
|
142
|
+
"Ick":http://ick.rubyforge.org provides abstractions for building your own invocations, so you can branch out and build some of your own abstractions with Ick's building blocks.
|
143
|
+
|
144
|
+
h2. How to submit patches
|
145
|
+
|
146
|
+
Read the "8 steps for fixing other people's code":http://drnicwilliams.com/2007/06/01/8-steps-for-fixing-other-peoples-code/.
|
147
|
+
|
148
|
+
The public clone url is @git://github.com/raganwald/andand.git@. Fork you very much.
|
149
|
+
|
150
|
+
h2. License
|
151
|
+
|
152
|
+
This code is free to use under the terms of the "MIT license":http://en.wikipedia.org/wiki/MIT_License.
|
153
|
+
|
154
|
+
h2. Shout Out
|
155
|
+
|
156
|
+
"Mobile Commons":http://mcommons.com/. Huge.
|
157
|
+
|
158
|
+
Also interesting: "Wicked":http://github.com/wideopenspaces/wicked
|
159
|
+
|
160
|
+
h2. Contact
|
161
|
+
|
162
|
+
Comments are welcome. Send an email to "Reginald Braithwaite":mailto:raganwald@gmail.com.
|
data/lib/andand.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__)
|
2
|
+
|
3
|
+
module AndAnd
|
4
|
+
|
5
|
+
# This module is included in Object, so each of these methods are added
|
6
|
+
# to Object when you require 'andand'. Each method is an *adverb*: they are
|
7
|
+
# intended to be enchained with another method, such as receiver.adverb.method
|
8
|
+
#
|
9
|
+
# The purpose of an adverb is to modify what the primary method returns.
|
10
|
+
#
|
11
|
+
# Adverbs also take blocks or procs, passing the receiver as an argument to the
|
12
|
+
# block or proc. They retain the same semantics with a block or proc as they
|
13
|
+
# do with a method. This behaviour weakly resembles a monad.
|
14
|
+
module ObjectGoodies
|
15
|
+
|
16
|
+
# Returns nil if its receiver is nil, regardless of whether nil actually handles the
|
17
|
+
# actual method ot what it might return.
|
18
|
+
#
|
19
|
+
# 'foo'.andand.size => 3
|
20
|
+
# nil.andand.size => nil
|
21
|
+
# 'foo'.andand { |s| s << 'bar' } => 'foobar'
|
22
|
+
# nil.andand { |s| s << 'bar' } => nil
|
23
|
+
def andand (p = nil)
|
24
|
+
if self
|
25
|
+
if block_given?
|
26
|
+
yield(self)
|
27
|
+
elsif p
|
28
|
+
p.to_proc.call(self)
|
29
|
+
else
|
30
|
+
self
|
31
|
+
end
|
32
|
+
else
|
33
|
+
if block_given? or p
|
34
|
+
self
|
35
|
+
else
|
36
|
+
MockReturningMe.new(self)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Invokes the method and returns the receiver if nothing is raised. Therefore,
|
42
|
+
# the purpose of calling the method is strictly for side effects. In the block
|
43
|
+
# form, it resembles #tap from Ruby 1.9, and is useful for debugging. It also
|
44
|
+
# resembles #returning from Rails, with slightly different syntax.
|
45
|
+
#
|
46
|
+
# Object.new.me do |o|
|
47
|
+
# def o.foo
|
48
|
+
# 'foo'
|
49
|
+
# end
|
50
|
+
# end
|
51
|
+
# => your new object
|
52
|
+
#
|
53
|
+
# In the method form, it is handy for chaining methods that don't ordinarily
|
54
|
+
# return the receiver:
|
55
|
+
#
|
56
|
+
# [1, 2, 3, 4, 5].me.pop.reverse
|
57
|
+
# => [4, 3, 2, 1]
|
58
|
+
def me (p = nil)
|
59
|
+
if block_given?
|
60
|
+
yield(self)
|
61
|
+
self
|
62
|
+
elsif p
|
63
|
+
p.to_proc.call(self)
|
64
|
+
self
|
65
|
+
else
|
66
|
+
ProxyReturningMe.new(self)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
unless Object.instance_methods.include?('tap')
|
71
|
+
alias :tap :me
|
72
|
+
end
|
73
|
+
|
74
|
+
# Does not invoke the method or block and returns the receiver.
|
75
|
+
# Useful for comemnting stuff out, especially if you are using #me for
|
76
|
+
# debugging purposes: change the .me to .dont and the semantics of your
|
77
|
+
# program are unchanged.
|
78
|
+
#
|
79
|
+
# [1, 2, 3, 4, 5].me { |x| p x }
|
80
|
+
# => prints and returns the array
|
81
|
+
# [1, 2, 3, 4, 5].dont { |x| p x }
|
82
|
+
# => returns the array without printing it
|
83
|
+
def dont (p = nil)
|
84
|
+
if block_given?
|
85
|
+
self
|
86
|
+
elsif p
|
87
|
+
self
|
88
|
+
else
|
89
|
+
MockReturningMe.new(self)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
class Object
|
98
|
+
include AndAnd::ObjectGoodies
|
99
|
+
end
|
100
|
+
|
101
|
+
unless Module.constants.map { |c| c.to_s }.include?('BlankSlate')
|
102
|
+
if Module.constants.map { |c| c.to_s }.include?('BasicObject')
|
103
|
+
module AndAnd
|
104
|
+
class BlankSlate < BasicObject
|
105
|
+
end
|
106
|
+
end
|
107
|
+
else
|
108
|
+
module AndAnd
|
109
|
+
class BlankSlate
|
110
|
+
def self.wipe
|
111
|
+
instance_methods.reject { |m| m =~ /^__/ }.each { |m| undef_method m }
|
112
|
+
end
|
113
|
+
def initialize
|
114
|
+
BlankSlate.wipe
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
module AndAnd
|
122
|
+
|
123
|
+
# A proxy that returns its target without invoking the method you
|
124
|
+
# invoke. Useful for nil.andand and #dont
|
125
|
+
class MockReturningMe < BlankSlate
|
126
|
+
def initialize(me)
|
127
|
+
super()
|
128
|
+
@me = me
|
129
|
+
end
|
130
|
+
def method_missing(*args)
|
131
|
+
@me
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# A proxy that returns its target after invoking the method you
|
136
|
+
# invoke. Useful for #me
|
137
|
+
class ProxyReturningMe < BlankSlate
|
138
|
+
def initialize(me)
|
139
|
+
super()
|
140
|
+
@me = me
|
141
|
+
end
|
142
|
+
def method_missing(sym, *args, &block)
|
143
|
+
@me.__send__(sym, *args, &block)
|
144
|
+
@me
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
data/test/andand_spec.rb
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
Copyright (c) 2008 Reginald Braithwaite
|
4
|
+
http://weblog.raganwald.com/2008/01/objectandand-objectme-in-ruby.html
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person
|
7
|
+
obtaining a copy of this software and associated documentation
|
8
|
+
files (the "Software"), to deal in the Software without
|
9
|
+
restriction, including without limitation the rights to use,
|
10
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
copies of the Software, and to permit persons to whom the
|
12
|
+
Software is furnished to do so, subject to the following
|
13
|
+
conditions:
|
14
|
+
|
15
|
+
The above copyright notice and this permission notice shall be
|
16
|
+
included in all copies or substantial portions of the Software.
|
17
|
+
|
18
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
19
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
20
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
21
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
22
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
23
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
24
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
25
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
26
|
+
|
27
|
+
=end
|
28
|
+
|
29
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
30
|
+
|
31
|
+
require 'andand'
|
32
|
+
|
33
|
+
class Symbol
|
34
|
+
def to_proc
|
35
|
+
Proc.new { |*args| args.shift.__send__(self, *args) }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe AndAnd, "Basic Behaviour" do
|
40
|
+
it "should conditionally yield to a block" do
|
41
|
+
5.andand { |n| n*n }.should == 25
|
42
|
+
5.andand(&:succ).should == 6
|
43
|
+
nil.andand { |n| n*n }.should == nil
|
44
|
+
false.andand { |n| n*n }.should == false
|
45
|
+
end
|
46
|
+
it "should conditionally call a proc-able parameter" do
|
47
|
+
5.andand(:succ).should == 6
|
48
|
+
nil.andand(:succ).should == nil
|
49
|
+
false.andand(:succ).should == false
|
50
|
+
end
|
51
|
+
it "should conditionally proxy methods" do
|
52
|
+
5.andand.succ.should == 6
|
53
|
+
nil.andand.succ.should == nil
|
54
|
+
false.andand.succ.should == false
|
55
|
+
(1..10).andand.inject(&:+).should == 55
|
56
|
+
nil.andand.inject(&:+).should == nil
|
57
|
+
false.andand.inject(&:+).should == false
|
58
|
+
'HelloWeblogReaders'.andand[7,4].should == 'blog'
|
59
|
+
nil.andand[7,4].should == nil
|
60
|
+
false.andand[7,4].should == false
|
61
|
+
'HelloWeblogReaders'.andand.tr('Wb','Bd').should == 'HelloBedlogReaders'
|
62
|
+
nil.andand.tr('Wb','Bd').should == nil
|
63
|
+
false.andand.tr('Wb','Bd').should == false
|
64
|
+
end
|
65
|
+
it "should handle infix operators" do
|
66
|
+
(5.andand * 2).should == 10
|
67
|
+
(nil.andand * 2).should == nil
|
68
|
+
(false.andand * 2).should == false
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe AndAnd, "Me" do
|
73
|
+
it "should unconditionally yield to a block" do
|
74
|
+
5.me { |n| n*n }.should == 5
|
75
|
+
5.me(&:succ).should == 5
|
76
|
+
nil.me { |n| n || true }.should be_nil
|
77
|
+
false.me { |n| n || true }.should == false
|
78
|
+
end
|
79
|
+
it "should unconditionally call a proc-able parameter" do
|
80
|
+
5.me(:to_s).should == 5
|
81
|
+
nil.me(:to_s).should == nil
|
82
|
+
false.me(:to_s).should == false
|
83
|
+
end
|
84
|
+
it "should proxy methods" do
|
85
|
+
5.me.to_s.should == 5
|
86
|
+
nil.me.to_s.should == nil
|
87
|
+
false.me.to_s.should == false
|
88
|
+
(1..10).me.inject(&:+).should == (1..10)
|
89
|
+
'HelloWeblogReaders'.me[7,4].should == 'HelloWeblogReaders'
|
90
|
+
'HelloWeblogReaders'.me.tr('Wb','Bd').should == 'HelloWeblogReaders'
|
91
|
+
end
|
92
|
+
it "should handle infix operators" do
|
93
|
+
(5.me * 2).should == 5
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe AndAnd, "Mixing Me with AndAnd" do
|
98
|
+
it "should compose me.andand" do
|
99
|
+
frobbish = :blitz
|
100
|
+
:foo.me.andand do
|
101
|
+
frobbish = :bar
|
102
|
+
end.should == :foo
|
103
|
+
frobbish.should == :bar
|
104
|
+
nil.me.andand do
|
105
|
+
frobbish = :barbarella
|
106
|
+
end.should be_nil
|
107
|
+
frobbish.should == :bar
|
108
|
+
false.me.andand do
|
109
|
+
frobbish = :hope
|
110
|
+
end.should == false
|
111
|
+
frobbish.should == :bar
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
class Foo
|
116
|
+
def frobbish
|
117
|
+
'fnord'
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe AndAnd, "exception handling" do
|
122
|
+
it "should not swallow NoMethodErrors" do
|
123
|
+
foo = Foo.new
|
124
|
+
lambda do
|
125
|
+
foo.andand.frobbish
|
126
|
+
nil.andand.frobbish
|
127
|
+
nil.andand.hsibborf
|
128
|
+
end.should_not raise_error
|
129
|
+
lambda do
|
130
|
+
foo.andand.hsibborf
|
131
|
+
end.should raise_error
|
132
|
+
end
|
133
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ryansch-andand
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 31
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 3
|
9
|
+
- 2
|
10
|
+
version: 1.3.2
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Reg Braithwaite
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2008-11-07 00:00:00 -08:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: " Maybe Monad in idiomatic Ruby."
|
23
|
+
email: reg@braythwayt.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files:
|
29
|
+
- History.txt
|
30
|
+
- README.textile
|
31
|
+
files:
|
32
|
+
- History.txt
|
33
|
+
- License.txt
|
34
|
+
- README.textile
|
35
|
+
- lib/andand.rb
|
36
|
+
- lib/andand/version.rb
|
37
|
+
- test/andand_spec.rb
|
38
|
+
- test/test_helper.rb
|
39
|
+
has_rdoc: true
|
40
|
+
homepage: http://github.com/ryansch/andand
|
41
|
+
licenses: []
|
42
|
+
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options:
|
45
|
+
- --main
|
46
|
+
- README.textile
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
hash: 3
|
55
|
+
segments:
|
56
|
+
- 0
|
57
|
+
version: "0"
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
hash: 3
|
64
|
+
segments:
|
65
|
+
- 0
|
66
|
+
version: "0"
|
67
|
+
requirements: []
|
68
|
+
|
69
|
+
rubyforge_project:
|
70
|
+
rubygems_version: 1.6.2
|
71
|
+
signing_key:
|
72
|
+
specification_version: 3
|
73
|
+
summary: The Maybe Monad in idiomatic Ruby
|
74
|
+
test_files:
|
75
|
+
- test/andand_spec.rb
|
76
|
+
- test/test_helper.rb
|