ryansch-andand 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|