andand 1.2.0 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +2 -0
- data/lib/andand.rb +4 -1
- data/lib/andand/version.rb +1 -1
- data/script/destroy +0 -0
- data/script/generate +0 -0
- data/script/txt2html +0 -0
- data/website/index.html +100 -46
- data/website/index.txt +48 -19
- data/website/template.rhtml +2 -0
- metadata +47 -40
data/History.txt
CHANGED
data/lib/andand.rb
CHANGED
@@ -57,8 +57,11 @@ end
|
|
57
57
|
module AndAnd
|
58
58
|
|
59
59
|
class BlankSlate
|
60
|
-
|
60
|
+
def self.wipe
|
61
|
+
instance_methods.reject { |m| m =~ /^__/ }.each { |m| undef_method m }
|
62
|
+
end
|
61
63
|
def initialize(me)
|
64
|
+
BlankSlate.wipe
|
62
65
|
@me = me
|
63
66
|
end
|
64
67
|
end
|
data/lib/andand/version.rb
CHANGED
data/script/destroy
CHANGED
File without changes
|
data/script/generate
CHANGED
File without changes
|
data/script/txt2html
CHANGED
File without changes
|
data/website/index.html
CHANGED
@@ -33,7 +33,7 @@
|
|
33
33
|
<h1>Object#andand</h1>
|
34
34
|
<div id="version" class="clickable" onclick='document.location = "http://rubyforge.org/projects/andand"; return false'>
|
35
35
|
<p>Get Version</p>
|
36
|
-
<a href="http://rubyforge.org/projects/andand" class="numbers">1.2.
|
36
|
+
<a href="http://rubyforge.org/projects/andand" class="numbers">1.2.1</a>
|
37
37
|
</div>
|
38
38
|
<h1>→ ‘andand’</h1>
|
39
39
|
|
@@ -44,11 +44,25 @@
|
|
44
44
|
<p><em>Object#andand</em> lets us write:</p>
|
45
45
|
|
46
46
|
|
47
|
-
<pre>
|
48
|
-
|
49
|
-
</pre>And get a <em>guarded method invocation</em> or <em>safe navigation method</em>. This snippet performs a <code>.find</code> on the Location class, then sends <code>.phone</code> to the result <em>if the result is not nil</em>. If the result is nil, then the expression returns nil without throwing a NoMethodError
|
47
|
+
<p><pre class='syntax'>
|
48
|
+
<span class="attribute">@phone</span> <span class="punct">=</span> <span class="constant">Location</span><span class="punct">.</span><span class="ident">find</span><span class="punct">(</span><span class="symbol">:first</span><span class="punct">,</span> <span class="punct">...</span><span class="ident">elided</span><span class="punct">...</span> <span class="punct">).</span><span class="ident">andand</span><span class="punct">.</span><span class="ident">phone</span>
|
49
|
+
</pre>And get a <em>guarded method invocation</em> or <em>safe navigation method</em>. This snippet performs a <code>.find</code> on the Location class, then sends <code>.phone</code> to the result <em>if the result is not nil</em>. If the result is nil, then the expression returns nil without throwing a NoMethodError.</p>
|
50
50
|
|
51
|
-
|
51
|
+
|
52
|
+
<p>As Dejan Simic <a href="http://rors.org/2008/3/18/andand">put it</a>:</p>
|
53
|
+
|
54
|
+
|
55
|
+
<p>Why would you want to write this:</p>
|
56
|
+
|
57
|
+
|
58
|
+
<p><pre class='syntax'>
|
59
|
+
<span class="ident">entry</span><span class="punct">.</span><span class="ident">at</span><span class="punct">('</span><span class="string">description</span><span class="punct">')</span> <span class="punct">&&</span> <span class="ident">entry</span><span class="punct">.</span><span class="ident">at</span><span class="punct">('</span><span class="string">description</span><span class="punct">').</span><span class="ident">inner_text</span>
|
60
|
+
</pre>when you can write this:</p>
|
61
|
+
|
62
|
+
|
63
|
+
<p><pre class='syntax'>
|
64
|
+
<span class="ident">entry</span><span class="punct">.</span><span class="ident">at</span><span class="punct">('</span><span class="string">description</span><span class="punct">').</span><span class="ident">andand</span><span class="punct">.</span><span class="ident">inner_text</span>
|
65
|
+
</pre>Why indeed! As a bonus, install andand and you will also receive an enhanced Object#tap method, <em>at no extra charge</em>!</p>
|
52
66
|
|
53
67
|
|
54
68
|
<h2>Installing</h2>
|
@@ -66,29 +80,52 @@
|
|
66
80
|
<p>Ruby programmers are familiar with the two <em>guarded assignment</em> operators <code>&&=</code> and <code>||=</code>. The typical use for them is when you have a variable that might be nil. For example:</p>
|
67
81
|
|
68
82
|
|
69
|
-
<pre>
|
70
|
-
first_name
|
71
|
-
|
72
|
-
</pre>You are trimming the first name provided it isn’t nil, and you are assigning ‘612-777-9311’ to the phone if it <em>is</em> 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 <em>guarded method invocation</em> operator. Here’s an example of when you would use it
|
83
|
+
<p><pre class='syntax'>
|
84
|
+
<span class="ident">first_name</span> <span class="punct">&&=</span> <span class="attribute">@first_name</span><span class="punct">.</span><span class="ident">trim</span>
|
85
|
+
<span class="attribute">@phone</span> <span class="punct">||=</span> <span class="punct">'</span><span class="string">612-777-9311</span><span class="punct">'</span>
|
86
|
+
</pre>You are trimming the first name provided it isn’t nil, and you are assigning ‘612-777-9311’ to the phone if it <em>is</em> 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 <em>guarded method invocation</em> operator. Here’s an example of when you would use it:</p>
|
87
|
+
|
88
|
+
|
89
|
+
<p><pre class='syntax'>
|
90
|
+
<span class="attribute">@phone</span> <span class="punct">=</span> <span class="constant">Location</span><span class="punct">.</span><span class="ident">find</span><span class="punct">(</span><span class="symbol">:first</span><span class="punct">,</span> <span class="punct">...</span><span class="ident">elided</span><span class="punct">...</span> <span class="punct">)&&.</span><span class="ident">phone</span>
|
91
|
+
</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 <code>?.</code> instead of <code>&&.</code>) However, <code>&&.</code> won’t work because <code>&&.</code> is not a real Ruby operator.</p>
|
73
92
|
|
74
|
-
<pre>
|
75
|
-
@phone = Location.find(:first, ...elided... )&&.phone
|
76
|
-
</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 <code>?.</code> instead of <code>&&.</code>) However, <code>&&.</code> won’t work because <code>&&.</code> is not a real Ruby operator.
|
77
93
|
|
78
94
|
<p>Object#andand let’s us write:</p>
|
79
95
|
|
80
96
|
|
81
|
-
<pre>
|
82
|
-
|
83
|
-
</pre>And get the same effect as
|
97
|
+
<p><pre class='syntax'>
|
98
|
+
<span class="attribute">@phone</span> <span class="punct">=</span> <span class="constant">Location</span><span class="punct">.</span><span class="ident">find</span><span class="punct">(</span><span class="symbol">:first</span><span class="punct">,</span> <span class="punct">...</span><span class="ident">elided</span><span class="punct">...</span> <span class="punct">).</span><span class="ident">andand</span><span class="punct">.</span><span class="ident">phone</span>
|
99
|
+
</pre>And get the same effect as:</p>
|
100
|
+
|
101
|
+
|
102
|
+
<p><pre class='syntax'>
|
103
|
+
<span class="attribute">@phone</span> <span class="punct">=</span> <span class="punct">->(</span><span class="ident">loc</span><span class="punct">){</span> <span class="ident">loc</span> <span class="punct">&&</span> <span class="ident">loc</span><span class="punct">.</span><span class="ident">phone</span> <span class="punct">}.</span><span class="ident">call</span><span class="punct">(</span><span class="constant">Location</span><span class="punct">.</span><span class="ident">find</span><span class="punct">(</span><span class="symbol">:first</span><span class="punct">,</span> <span class="punct">...</span><span class="ident">elided</span><span class="punct">...</span> <span class="punct">))</span>
|
104
|
+
</pre>Note that because you accept any method using Ruby’s method invocation syntax, you can accept methods with parameters and/or blocks:</p>
|
105
|
+
|
106
|
+
|
107
|
+
<p><pre class='syntax'>
|
108
|
+
<span class="ident">list_of_lists</span><span class="punct">.</span><span class="ident">detect</span> <span class="punct">{</span> <span class="punct">...</span><span class="ident">elided</span><span class="punct">...</span> <span class="punct">}.</span><span class="ident">andand</span><span class="punct">.</span><span class="ident">inject</span><span class="punct">(</span><span class="number">42</span><span class="punct">)</span> <span class="punct">{</span> <span class="punct">...</span><span class="ident">elided</span> <span class="punct">...</span> <span class="punct">}</span>
|
109
|
+
</pre>Object#andand emphasizes syntactic regularity: the goal was to make an <code>&&.</code> operation that worked like <code>&&=</code>. <code>&&=</code> looks just like normal assignment, you can use any expression on the <span class="caps">RHS</span>, only the semantics are different. The andand method also works just like a normal method invocation, only the semantics are modified.</p>
|
110
|
+
|
111
|
+
|
112
|
+
<h3>Use andand to simplify your regular expression matching and extraction</h3>
|
113
|
+
|
114
|
+
|
115
|
+
<p>Do you ever find yourself wanting to extract a single value from a string using a regular expression? For example, Ruby’s Tempfile class creates paths to files that end in .pid.n (where pid is your process id and n is some number). Do you have a path that might be a tempfile and you want to obtain the base name?</p>
|
84
116
|
|
85
|
-
<pre>
|
86
|
-
@phone = ->(loc){ loc && loc.phone }.call(Location.find(:first, ...elided... ))
|
87
|
-
</pre>Note that because you accept any method using Ruby’s method invocation syntax, you can accept methods with parameters and/or blocks:
|
88
117
|
|
89
|
-
<
|
90
|
-
|
91
|
-
|
118
|
+
<p>Do you currently retrieve the MatchData object, check if it is nil, and get the first matching group if it isn’t? How about:</p>
|
119
|
+
|
120
|
+
|
121
|
+
<p><pre class='syntax'>
|
122
|
+
<span class="ident">require</span> <span class="punct">'</span><span class="string">tempfile</span><span class="punct">'</span>
|
123
|
+
<span class="ident">path</span> <span class="punct">=</span> <span class="constant">Tempfile</span><span class="punct">.</span><span class="ident">new</span><span class="punct">('</span><span class="string">foo.bar</span><span class="punct">').</span><span class="ident">path</span>
|
124
|
+
<span class="punct">=></span> <span class="punct">"</span><span class="string">/var/folders/UZ/UZyZsbVPEWqC7tTXrQBYGU+++TI/-Tmp-/foo.bar.1280.0</span><span class="punct">"</span>
|
125
|
+
<span class="ident">path</span><span class="punct">.</span><span class="ident">match</span><span class="punct">('</span><span class="string">/([^/]+)\.[0-9]+\.[0-9]+$</span><span class="punct">').</span><span class="ident">andand</span><span class="punct">[</span><span class="number">1</span><span class="punct">]</span>
|
126
|
+
<span class="punct">=></span> <span class="punct">"</span><span class="string">foo.bar</span><span class="punct">"</span>
|
127
|
+
</pre>With <code>.andand[1]</code>, you extract the group in the regular expression safely: if the expression matches, you get the group. If the expression fails to match, you get nil. Which is what you want, isn’t it? The contents of the group if the expression matches? Why should you need more than one line for something so simple?</p>
|
128
|
+
|
92
129
|
|
93
130
|
<h3>Enhanced Object#tap</h3>
|
94
131
|
|
@@ -96,47 +133,62 @@ list_of_lists.detect { ...elided... }.andand.inject(42) { ...elided ... }
|
|
96
133
|
<p>Ruby 1.9 introduces <a href="http://moonbase.rydia.net/mental/blog/programming/eavesdropping-on-expressions">Object#tap</a>. This library implements Object#tap for Ruby 1.8 <strong>and</strong> enhances it. As in Ruby 1.9, you can call <code>.tap</code> with a block:</p>
|
97
134
|
|
98
135
|
|
99
|
-
<pre>
|
100
|
-
|
101
|
-
</pre> But like its sibling <code>.andand</code>, you can now call <code>.tap</code> with a method as well
|
136
|
+
<p><pre class='syntax'>
|
137
|
+
<span class="ident">blah</span><span class="punct">.</span><span class="ident">sort</span><span class="punct">.</span><span class="ident">grep</span><span class="punct">(</span> <span class="punct">/</span><span class="regex">foo</span><span class="punct">/</span> <span class="punct">).</span><span class="ident">tap</span> <span class="punct">{</span> <span class="punct">|</span><span class="ident">xs</span><span class="punct">|</span> <span class="ident">p</span> <span class="ident">xs</span> <span class="punct">}.</span><span class="ident">map</span> <span class="punct">{</span> <span class="punct">|</span><span class="ident">x</span><span class="punct">|</span> <span class="ident">x</span><span class="punct">.</span><span class="ident">blah</span> <span class="punct">}</span>
|
138
|
+
</pre> But like its sibling <code>.andand</code>, you can now call <code>.tap</code> with a method as well:</p>
|
139
|
+
|
140
|
+
|
141
|
+
<p><pre class='syntax'>
|
142
|
+
<span class="punct">[</span><span class="number">1</span><span class="punct">,</span> <span class="number">2</span><span class="punct">,</span> <span class="number">3</span><span class="punct">,</span> <span class="number">4</span><span class="punct">,</span> <span class="number">5</span><span class="punct">].</span><span class="ident">tap</span><span class="punct">.</span><span class="ident">pop</span><span class="punct">.</span><span class="ident">map</span> <span class="punct">{</span> <span class="punct">|</span><span class="ident">n</span><span class="punct">|</span> <span class="ident">n</span> <span class="punct">*</span> <span class="number">2</span> <span class="punct">}</span>
|
143
|
+
<span class="punct">=></span> <span class="punct">[</span><span class="number">2</span><span class="punct">,</span> <span class="number">4</span><span class="punct">,</span> <span class="number">6</span><span class="punct">,</span> <span class="number">8</span><span class="punct">]</span>
|
144
|
+
</pre></p>
|
102
145
|
|
103
|
-
<pre>
|
104
|
-
[1, 2, 3, 4, 5].tap.pop.map { |n| n * 2 }
|
105
|
-
=> [2, 4, 6, 8]
|
106
|
-
</pre>
|
107
146
|
|
108
147
|
<h3>Doctor, it hurts when I do that</h3>
|
109
148
|
|
110
149
|
|
111
|
-
<p><
|
150
|
+
<p><em>So don’t do that!</em></p>
|
112
151
|
|
113
152
|
|
114
153
|
<p>The popular use case for Object#tap is poor man’s debugging:</p>
|
115
154
|
|
116
155
|
|
117
|
-
<pre>
|
118
|
-
|
119
|
-
</pre>Perhaps you want to remove the tap, you can delete it
|
156
|
+
<p><pre class='syntax'>
|
157
|
+
<span class="ident">blah</span><span class="punct">.</span><span class="ident">sort</span><span class="punct">.</span><span class="ident">grep</span><span class="punct">(</span> <span class="punct">/</span><span class="regex">foo</span><span class="punct">/</span> <span class="punct">).</span><span class="ident">tap</span> <span class="punct">{</span> <span class="punct">|</span><span class="ident">xs</span><span class="punct">|</span> <span class="ident">p</span> <span class="ident">xs</span> <span class="punct">}.</span><span class="ident">map</span> <span class="punct">{</span> <span class="punct">|</span><span class="ident">x</span><span class="punct">|</span> <span class="ident">x</span><span class="punct">.</span><span class="ident">blah</span> <span class="punct">}</span>
|
158
|
+
</pre>Perhaps you want to remove the tap, you can delete it:</p>
|
159
|
+
|
160
|
+
|
161
|
+
<p><pre class='syntax'>
|
162
|
+
<span class="ident">blah</span><span class="punct">.</span><span class="ident">sort</span><span class="punct">.</span><span class="ident">grep</span><span class="punct">(</span> <span class="punct">/</span><span class="regex">foo</span><span class="punct">/</span> <span class="punct">).</span><span class="ident">map</span> <span class="punct">{</span> <span class="punct">|</span><span class="ident">x</span><span class="punct">|</span> <span class="ident">x</span><span class="punct">.</span><span class="ident">blah</span> <span class="punct">}</span>
|
163
|
+
</pre>Or, you can change it to <code>.dont</code>:</p>
|
164
|
+
|
120
165
|
|
121
|
-
<pre>
|
122
|
-
|
123
|
-
</pre>
|
166
|
+
<p><pre class='syntax'>
|
167
|
+
<span class="ident">blah</span><span class="punct">.</span><span class="ident">sort</span><span class="punct">.</span><span class="ident">grep</span><span class="punct">(</span> <span class="punct">/</span><span class="regex">foo</span><span class="punct">/</span> <span class="punct">).</span><span class="ident">dont</span> <span class="punct">{</span> <span class="punct">|</span><span class="ident">xs</span><span class="punct">|</span> <span class="ident">p</span> <span class="ident">xs</span> <span class="punct">}.</span><span class="ident">map</span> <span class="punct">{</span> <span class="punct">|</span><span class="ident">x</span><span class="punct">|</span> <span class="ident">x</span><span class="punct">.</span><span class="ident">blah</span> <span class="punct">}</span>
|
168
|
+
</pre>Like <code>.andand</code> and <code>.tap</code>, <code>.dont</code> works with arbitrary methods, not just blocks:</p>
|
124
169
|
|
125
|
-
<pre>
|
126
|
-
blah.sort.grep( /foo/ ).dont { |xs| p xs }.map { |x| x.blah }
|
127
|
-
</pre>Like <code>.andand</code> and <code>.tap</code>, <code>.dont</code> works with arbitrary methods, not just blocks:
|
128
170
|
|
129
|
-
<pre>
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
</pre>
|
171
|
+
<p><pre class='syntax'>
|
172
|
+
<span class="punct">(</span><span class="number">1</span><span class="punct">..</span><span class="number">10</span><span class="punct">).</span><span class="ident">to_a</span><span class="punct">.</span><span class="ident">reverse!</span>
|
173
|
+
<span class="punct">=></span> <span class="punct">[</span><span class="number">10</span><span class="punct">,</span> <span class="number">9</span><span class="punct">,</span> <span class="number">8</span><span class="punct">,</span> <span class="number">7</span><span class="punct">,</span> <span class="number">6</span><span class="punct">,</span> <span class="number">5</span><span class="punct">,</span> <span class="number">4</span><span class="punct">,</span> <span class="number">3</span><span class="punct">,</span> <span class="number">2</span><span class="punct">,</span> <span class="number">1</span><span class="punct">]</span>
|
174
|
+
<span class="punct">(</span><span class="number">1</span><span class="punct">..</span><span class="number">10</span><span class="punct">).</span><span class="ident">to_a</span><span class="punct">.</span><span class="ident">dont</span><span class="punct">.</span><span class="ident">reverse!</span>
|
175
|
+
<span class="punct">=></span> <span class="punct">[</span><span class="number">1</span><span class="punct">,</span> <span class="number">2</span><span class="punct">,</span> <span class="number">3</span><span class="punct">,</span> <span class="number">4</span><span class="punct">,</span> <span class="number">5</span><span class="punct">,</span> <span class="number">6</span><span class="punct">,</span> <span class="number">7</span><span class="punct">,</span> <span class="number">8</span><span class="punct">,</span> <span class="number">9</span><span class="punct">,</span> <span class="number">10</span><span class="punct">]</span>
|
176
|
+
</pre></p>
|
177
|
+
|
135
178
|
|
136
179
|
<h2>A little more background</h2>
|
137
180
|
|
138
181
|
|
139
|
-
<p><a href="http://weblog.raganwald.com/2008/01/objectandand-objectme-in-ruby.html">Object#andand & Object#me in Ruby</a> 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
|
182
|
+
<p><a href="http://weblog.raganwald.com/2008/01/objectandand-objectme-in-ruby.html">Object#andand & Object#me in Ruby</a> 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. <a href="http://blog.pretheory.com/arch/2008/02/the_maybe_monad_in_ruby.php">The Maybe Monad in Ruby</a> is a good introduction for Ruby programmers.</p>
|
183
|
+
|
184
|
+
|
185
|
+
<h2>That’s cool, but…</h2>
|
186
|
+
|
187
|
+
|
188
|
+
<p>No problem, I get that andand isn’t exactly what you need. Have a look at the <a href="http://ick.rubyforge.org">Invocation Construction Kit</a> or “Ick.” The Ick gem <em>generalizes</em> #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.</p>
|
189
|
+
|
190
|
+
|
191
|
+
<p><a href="http://ick.rubyforge.org">Ick</a> provides abstractions for building your own invocations, so you can branch out and build some of your own abstractions with Ick’s building blocks.</p>
|
140
192
|
|
141
193
|
|
142
194
|
<h2>How to submit patches</h2>
|
@@ -165,12 +217,14 @@ list_of_lists.detect { ...elided... }.andand.inject(42) { ...elided ... }
|
|
165
217
|
|
166
218
|
<p>Comments are welcome. Send an email to <a href="mailto:raganwald+rubyforge@gmail.com">Reginald Braithwaite</a>.</p>
|
167
219
|
<p class="coda">
|
168
|
-
<a href="http://weblog.raganwald.com">Reginald Braithwaite</a>,
|
220
|
+
<a href="http://weblog.raganwald.com">Reginald Braithwaite</a>, 6th April 2008<br>
|
169
221
|
Theme extended from <a href="http://rb2js.rubyforge.org/">Paul Battley</a>
|
170
222
|
</p>
|
171
223
|
</div>
|
172
224
|
|
173
225
|
<!-- insert site tracking codes here, like Google Urchin -->
|
174
226
|
|
227
|
+
<div style="visibility: hidden"><script type="text/javascript" src="http://pub44.bravenet.com/counter/code.php?id=404723&usernum=3754613835&cpv=2"></script></div>
|
228
|
+
|
175
229
|
</body>
|
176
230
|
</html>
|
data/website/index.txt
CHANGED
@@ -6,11 +6,21 @@ h2. What
|
|
6
6
|
|
7
7
|
_Object#andand_ lets us write:
|
8
8
|
|
9
|
-
<pre>
|
9
|
+
<pre syntax="ruby">
|
10
10
|
@phone = Location.find(:first, ...elided... ).andand.phone
|
11
11
|
</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.
|
12
12
|
|
13
|
-
|
13
|
+
As Dejan Simic "put it":http://rors.org/2008/3/18/andand:
|
14
|
+
|
15
|
+
Why would you want to write this:
|
16
|
+
|
17
|
+
<pre syntax="ruby">
|
18
|
+
entry.at('description') && entry.at('description').inner_text
|
19
|
+
</pre>when you can write this:
|
20
|
+
|
21
|
+
<pre syntax="ruby">
|
22
|
+
entry.at('description').andand.inner_text
|
23
|
+
</pre>Why indeed! As a bonus, install andand and you will also receive an enhanced Object#tap method, _at no extra charge_!
|
14
24
|
|
15
25
|
h2. Installing
|
16
26
|
|
@@ -22,70 +32,90 @@ h3. Object#andand
|
|
22
32
|
|
23
33
|
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:
|
24
34
|
|
25
|
-
<pre>
|
35
|
+
<pre syntax="ruby">
|
26
36
|
first_name &&= @first_name.trim
|
27
37
|
@phone ||= '612-777-9311'
|
28
38
|
</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:
|
29
39
|
|
30
|
-
<pre>
|
40
|
+
<pre syntax="ruby">
|
31
41
|
@phone = Location.find(:first, ...elided... )&&.phone
|
32
42
|
</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.
|
33
43
|
|
34
44
|
Object#andand let’s us write:
|
35
45
|
|
36
|
-
<pre>
|
46
|
+
<pre syntax="ruby">
|
37
47
|
@phone = Location.find(:first, ...elided... ).andand.phone
|
38
48
|
</pre>And get the same effect as:
|
39
49
|
|
40
|
-
<pre>
|
50
|
+
<pre syntax="ruby">
|
41
51
|
@phone = ->(loc){ loc && loc.phone }.call(Location.find(:first, ...elided... ))
|
42
52
|
</pre>Note that because you accept any method using Ruby’s method invocation syntax, you can accept methods with parameters and/or blocks:
|
43
53
|
|
44
|
-
<pre>
|
54
|
+
<pre syntax="ruby">
|
45
55
|
list_of_lists.detect { ...elided... }.andand.inject(42) { ...elided ... }
|
46
56
|
</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.
|
47
57
|
|
58
|
+
h3. Use andand to simplify your regular expression matching and extraction
|
59
|
+
|
60
|
+
Do you ever find yourself wanting to extract a single value from a string using a regular expression? For example, Ruby's Tempfile class creates paths to files that end in .pid.n (where pid is your process id and n is some number). Do you have a path that might be a tempfile and you want to obtain the base name?
|
61
|
+
|
62
|
+
Do you currently retrieve the MatchData object, check if it is nil, and get the first matching group if it isn't? How about:
|
63
|
+
|
64
|
+
<pre syntax="ruby">
|
65
|
+
require 'tempfile'
|
66
|
+
path = Tempfile.new('foo.bar').path
|
67
|
+
=> "/var/folders/UZ/UZyZsbVPEWqC7tTXrQBYGU+++TI/-Tmp-/foo.bar.1280.0"
|
68
|
+
path.match('/([^/]+)\.[0-9]+\.[0-9]+$').andand[1]
|
69
|
+
=> "foo.bar"
|
70
|
+
</pre>With @.andand[1]@, you extract the group in the regular expression safely: if the expression matches, you get the group. If the expression fails to match, you get nil. Which is what you want, isn't it? The contents of the group if the expression matches? Why should you need more than one line for something so simple?
|
71
|
+
|
48
72
|
h3. Enhanced Object#tap
|
49
73
|
|
50
74
|
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:
|
51
75
|
|
52
|
-
<pre>
|
76
|
+
<pre syntax="ruby">
|
53
77
|
blah.sort.grep( /foo/ ).tap { |xs| p xs }.map { |x| x.blah }
|
54
78
|
</pre> But like its sibling @.andand@, you can now call @.tap@ with a method as well:
|
55
79
|
|
56
|
-
<pre>
|
80
|
+
<pre syntax="ruby">
|
57
81
|
[1, 2, 3, 4, 5].tap.pop.map { |n| n * 2 }
|
58
82
|
=> [2, 4, 6, 8]
|
59
83
|
</pre>
|
60
84
|
|
61
85
|
h3. Doctor, it hurts when I do that
|
62
86
|
|
63
|
-
|
87
|
+
_So don't do that!_
|
64
88
|
|
65
89
|
The popular use case for Object#tap is poor man's debugging:
|
66
90
|
|
67
|
-
<pre>
|
91
|
+
<pre syntax="ruby">
|
68
92
|
blah.sort.grep( /foo/ ).tap { |xs| p xs }.map { |x| x.blah }
|
69
93
|
</pre>Perhaps you want to remove the tap, you can delete it:
|
70
94
|
|
71
|
-
<pre>
|
72
|
-
blah.sort.grep( /foo/ ).
|
95
|
+
<pre syntax="ruby">
|
96
|
+
blah.sort.grep( /foo/ ).map { |x| x.blah }
|
73
97
|
</pre>Or, you can change it to @.dont@:
|
74
98
|
|
75
|
-
<pre>
|
99
|
+
<pre syntax="ruby">
|
76
100
|
blah.sort.grep( /foo/ ).dont { |xs| p xs }.map { |x| x.blah }
|
77
101
|
</pre>Like @.andand@ and @.tap@, @.dont@ works with arbitrary methods, not just blocks:
|
78
102
|
|
79
|
-
<pre>
|
103
|
+
<pre syntax="ruby">
|
80
104
|
(1..10).to_a.reverse!
|
81
105
|
=> [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
|
82
|
-
(1..10).dont.
|
106
|
+
(1..10).to_a.dont.reverse!
|
83
107
|
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
84
108
|
</pre>
|
85
109
|
|
86
110
|
h2. A little more background
|
87
111
|
|
88
|
-
"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
|
112
|
+
"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.
|
113
|
+
|
114
|
+
h2. That's cool, but…
|
115
|
+
|
116
|
+
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.
|
117
|
+
|
118
|
+
"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.
|
89
119
|
|
90
120
|
h2. How to submit patches
|
91
121
|
|
@@ -103,5 +133,4 @@ h2. Shout Out
|
|
103
133
|
|
104
134
|
h2. Contact
|
105
135
|
|
106
|
-
Comments are welcome. Send an email to "Reginald Braithwaite":mailto:raganwald+rubyforge@gmail.com.
|
107
|
-
|
136
|
+
Comments are welcome. Send an email to "Reginald Braithwaite":mailto:raganwald+rubyforge@gmail.com.
|
data/website/template.rhtml
CHANGED
metadata
CHANGED
@@ -1,34 +1,31 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.9.0
|
3
|
-
specification_version: 1
|
4
2
|
name: andand
|
5
3
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 1.2.
|
7
|
-
date: 2008-02-15 00:00:00 -05:00
|
8
|
-
summary: adds guarded method invocation to Ruby
|
9
|
-
require_paths:
|
10
|
-
- lib
|
11
|
-
email:
|
12
|
-
- reg@braythwayt.com
|
13
|
-
homepage: http://andand.rubyforge.org
|
14
|
-
rubyforge_project: andand
|
15
|
-
description: adds guarded method invocation to Ruby
|
16
|
-
autorequire:
|
17
|
-
default_executable:
|
18
|
-
bindir: bin
|
19
|
-
has_rdoc: true
|
20
|
-
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
21
|
-
requirements:
|
22
|
-
- - ">"
|
23
|
-
- !ruby/object:Gem::Version
|
24
|
-
version: 0.0.0
|
25
|
-
version:
|
4
|
+
version: 1.2.1
|
26
5
|
platform: ruby
|
27
|
-
signing_key:
|
28
|
-
cert_chain:
|
29
|
-
post_install_message:
|
30
6
|
authors:
|
31
7
|
- Reginald Braithwaite
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-07-10 00:00:00 -04:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: adds guarded method invocation to Ruby
|
17
|
+
email:
|
18
|
+
- reg@braythwayt.com
|
19
|
+
executables: []
|
20
|
+
|
21
|
+
extensions: []
|
22
|
+
|
23
|
+
extra_rdoc_files:
|
24
|
+
- History.txt
|
25
|
+
- License.txt
|
26
|
+
- Manifest.txt
|
27
|
+
- README.txt
|
28
|
+
- website/index.txt
|
32
29
|
files:
|
33
30
|
- History.txt
|
34
31
|
- License.txt
|
@@ -54,23 +51,33 @@ files:
|
|
54
51
|
- website/javascripts/rounded_corners_lite.inc.js
|
55
52
|
- website/stylesheets/screen.css
|
56
53
|
- website/template.rhtml
|
57
|
-
|
58
|
-
|
59
|
-
|
54
|
+
has_rdoc: true
|
55
|
+
homepage: http://andand.rubyforge.org
|
56
|
+
post_install_message:
|
60
57
|
rdoc_options:
|
61
58
|
- --main
|
62
59
|
- README.txt
|
63
|
-
|
64
|
-
-
|
65
|
-
|
66
|
-
|
67
|
-
-
|
68
|
-
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: "0"
|
67
|
+
version:
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: "0"
|
73
|
+
version:
|
73
74
|
requirements: []
|
74
75
|
|
75
|
-
|
76
|
-
|
76
|
+
rubyforge_project: andand
|
77
|
+
rubygems_version: 1.2.0
|
78
|
+
signing_key:
|
79
|
+
specification_version: 2
|
80
|
+
summary: adds guarded method invocation to Ruby
|
81
|
+
test_files:
|
82
|
+
- test/test_andand.rb
|
83
|
+
- test/test_helper.rb
|