ick 0.2.3 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,159 @@
1
+ h1. Ick in 180 seconds or less
2
+
3
+ h2. Transforming your code
4
+
5
+ Object#let turns:
6
+
7
+ <pre syntax="ruby">
8
+ </pre>
9
+
10
+ Into:
11
+
12
+ <pre syntax="ruby">
13
+ </pre>
14
+
15
+ Object#returning turns:
16
+
17
+ <pre syntax="ruby">
18
+ </pre>
19
+
20
+ Into:
21
+
22
+ <pre syntax="ruby">
23
+ </pre>
24
+
25
+ Object#my turns:
26
+
27
+ <pre syntax="ruby">
28
+ </pre>
29
+
30
+ Into:
31
+
32
+ <pre syntax="ruby">
33
+ </pre>
34
+
35
+ Object#inside turns:
36
+
37
+ <pre syntax="ruby">
38
+ </pre>
39
+
40
+ Into:
41
+
42
+ <pre syntax="ruby">
43
+ </pre>
44
+
45
+ h2. Test Cases
46
+
47
+ The four canonical block structuring methods (#let, #returning, #my, and #inside):
48
+
49
+ <pre syntax="ruby">
50
+ Ick.sugarize
51
+ arr = []
52
+
53
+ # let returns the value of the block and executes in the current environment
54
+ assert_equal(
55
+ "3628800 ends with zero",
56
+ let((1..10).inject(&:*)) { |num|
57
+ arr << self.class
58
+ num % 10 == 0 ? "#{num} ends with zero" : "#{num} does not end with zero"
59
+ })
60
+ assert_equal([ self.class ], arr)
61
+
62
+ # returning returns the value of the expression and executes in the current environment
63
+ assert_equal(
64
+ 3628800,
65
+ returning((1..10).inject(&:*)) { |num|
66
+ arr << self.class
67
+ num % 10 == 0 ? "#{num} ends with zero" : "#{num} does not end with zero"
68
+ })
69
+ assert_equal([ self.class, self.class ], arr)
70
+
71
+ # my returns the value of the block and executes in the expression's environment
72
+ assert_equal(
73
+ "3628800 ends with zero",
74
+ my((1..10).inject(&:*)) { |num|
75
+ arr << self.class
76
+ num % 10 == 0 ? "#{num} ends with zero" : "#{num} does not end with zero"
77
+ })
78
+ assert_equal([ self.class, self.class, Fixnum ], arr)
79
+
80
+ # inside returns the value of the expression and executes in the expression's environment
81
+ assert_equal(
82
+ 3628800,
83
+ inside((1..10).inject(&:*)) { |num|
84
+ arr << self.class
85
+ num % 10 == 0 ? "#{num} ends with zero" : "#{num} does not end with zero"
86
+ })
87
+ assert_equal([ self.class, self.class, Fixnum, Fixnum ], arr)
88
+ </pre>
89
+
90
+ The two most common guarded evauators (#maybe and #try):
91
+
92
+ <pre syntax="ruby">
93
+ # an object with two methods. #do returns itself, #do_not returns nil
94
+ yoda = returning(Object.new) do |obj|
95
+ def obj.do
96
+ self
97
+ end
98
+ def obj.do_not
99
+ nil
100
+ end
101
+ end
102
+
103
+ # maybe handles nils, even for methods nil has, but not missing methods
104
+ assert_equal(
105
+ yoda,
106
+ maybe(yoda) { |obj| obj.do }
107
+ )
108
+ assert_nil(
109
+ maybe(nil) { |obj| obj.do }
110
+ )
111
+ assert_nil(
112
+ maybe(yoda) { |obj| obj.do_not.do }
113
+ )
114
+ assert_nil(
115
+ maybe(nil) { |obj| obj.nil? }
116
+ )
117
+ assert_raise(NoMethodError) do
118
+ maybe(yoda) { |obj| obj.fubar }
119
+ end
120
+
121
+ #try handles NoMethodErrors, without caring whether nil is involved
122
+ assert_equal(
123
+ yoda,
124
+ try(yoda) { |obj| obj.do }
125
+ )
126
+ assert_nil(
127
+ try(nil) { |obj| obj.do_not }
128
+ )
129
+ assert_not_nil(
130
+ try(nil) { |obj| obj.nil? }
131
+ )
132
+ assert_nil(
133
+ try(yoda) { |obj| obj.fubar }
134
+ )
135
+ </pre>
136
+
137
+ h2. Administrivia
138
+
139
+ h3. Home Sweet Home
140
+
141
+ "ick.rubyforge.org":http://ick.rubyforge.org
142
+
143
+ h3. How to submit patches
144
+
145
+ Read the "8 steps for fixing other people's code":http://drnicwilliams.com/2007/06/01/8-steps-for-fixing-other-peoples-code/.
146
+
147
+ The trunk repository is @svn://rubyforge.org/var/svn/ick/trunk@ for anonymous access.
148
+
149
+ h3. License
150
+
151
+ This code is free to use under the terms of the "MIT license":http://en.wikipedia.org/wiki/MIT_License.
152
+
153
+ h3. Shout Out
154
+
155
+ "Mobile Commons":http://mcommons.com/. Still Huge After All These Years.
156
+
157
+ h3. Contact
158
+
159
+ Comments are welcome. Send an email to "Reginald Braithwaite":mailto:raganwald+rubyforge@gmail.com. And you can always visit "weblog.raganwald.com":http://weblog.raganwald.com/ to see what's cooking.
@@ -33,7 +33,7 @@
33
33
  <h1>Invocation Construction Kit</h1>
34
34
  <div id="version" class="clickable" onclick='document.location = "http://rubyforge.org/projects/ick"; return false'>
35
35
  <p>Get Version</p>
36
- <a href="http://rubyforge.org/projects/ick" class="numbers">0.2.3</a>
36
+ <a href="http://rubyforge.org/projects/ick" class="numbers">0.2.4</a>
37
37
  </div>
38
38
  <h1>&#x2192; &#8216;ick&#8217;</h1>
39
39
 
@@ -55,11 +55,14 @@
55
55
  </pre></p>
56
56
 
57
57
 
58
- <h2>Installing</h2>
58
+ <h2>Getting Started</h2>
59
59
 
60
60
 
61
61
  <pre>sudo gem install ick</pre>
62
62
 
63
+ <p>You can also see <a href="180seconds.html">Ick in 180 seconds or less</a></p>
64
+
65
+
63
66
  <h2>Block Structured Ruby</h2>
64
67
 
65
68
 
@@ -202,6 +205,12 @@
202
205
  <p><a href="inside.html">Inside Ick</a></p>
203
206
 
204
207
 
208
+ <h2>That&#8217;s cool, but&hellip;</h2>
209
+
210
+
211
+ <p>No problem, I get that Ick isn&#8217;t exactly what you need. Why not have a look at <a href="http://andand.rubyforge.org">andand</a>? The andand gem gives you a very specialized version of Object#maybe and an enhanced Object#tap: it does a lot less but tries to do it very, very well. Have a look and let me know what you think.</p>
212
+
213
+
205
214
  <h2>Administrivia</h2>
206
215
 
207
216
 
@@ -237,12 +246,14 @@
237
246
 
238
247
  <p>Comments are welcome. Send an email to <a href="mailto:raganwald+rubyforge@gmail.com">Reginald Braithwaite</a>. And you can always visit <a href="http://weblog.raganwald.com/">weblog.raganwald.com</a> to see what&#8217;s cooking.</p>
239
248
  <p class="coda">
240
- <a href="http://weblog.raganwald.com/">Reginald Braithwaite</a>, 8th March 2008<br>
249
+ <a href="http://weblog.raganwald.com/">Reginald Braithwaite</a>, 26th March 2008<br>
241
250
  Theme extended from <a href="http://rb2js.rubyforge.org/">Paul Battley</a>
242
251
  </p>
243
252
  </div>
244
253
 
245
254
  <!-- insert site tracking codes here, like Google Urchin -->
246
255
 
256
+ <script type="text/javascript" src="http://pub44.bravenet.com/counter/code.php?id=404724&usernum=3754613835&cpv=2"></script>
257
+
247
258
  </body>
248
259
  </html>
@@ -14,10 +14,12 @@ Thus, the *Invocation Construction Kit*, or "Ick!" Ick provides the tools needed
14
14
  please(sir) { may.i.have.some.more }
15
15
  </pre>
16
16
 
17
- h2. Installing
17
+ h2. Getting Started
18
18
 
19
19
  <pre>sudo gem install ick</pre>
20
20
 
21
+ You can also see "Ick in 180 seconds or less":180seconds.html
22
+
21
23
  h2. Block Structured Ruby
22
24
 
23
25
  Although Ruby borrows many of its features from Lisp and its syntax from Algol, it does not have block-local variables. In other words, if you declare a variable anywhere inside of a method, that variable is visible everywhere in that method. This is a problem, because it encourages writing methods where the instance variables create lot of dependencies between different expressions. Those methods can be hard to understand and refactor.
@@ -124,6 +126,10 @@ h2. Where can I read about what's going on inside Ick?
124
126
 
125
127
  "Inside Ick":inside.html
126
128
 
129
+ h2. That's cool, but&hellip;
130
+
131
+ No problem, I get that Ick isn't exactly what you need. Why not have a look at "andand":http://andand.rubyforge.org? The andand gem gives you a very specialized version of Object#maybe and an enhanced Object#tap: it does a lot less but tries to do it very, very well. Have a look and let me know what you think.
132
+
127
133
  h2. Administrivia
128
134
 
129
135
  h3. Home Sweet Home
@@ -0,0 +1,325 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
4
+ <head>
5
+ <link rel="stylesheet" href="stylesheets/screen.css" type="text/css" media="screen" />
6
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
7
+ <title>
8
+ Inside the Invocation Construction Kit
9
+ </title>
10
+ <script src="javascripts/rounded_corners_lite.inc.js" type="text/javascript"></script>
11
+ <style>
12
+
13
+ </style>
14
+ <script type="text/javascript">
15
+ window.onload = function() {
16
+ settings = {
17
+ tl: { radius: 10 },
18
+ tr: { radius: 10 },
19
+ bl: { radius: 10 },
20
+ br: { radius: 10 },
21
+ antiAlias: true,
22
+ autoPad: true,
23
+ validTags: ["div"]
24
+ }
25
+ var versionBox = new curvyCorners(settings, document.getElementById("version"));
26
+ versionBox.applyCornersToAll();
27
+ }
28
+ </script>
29
+ </head>
30
+ <body>
31
+ <div id="main">
32
+
33
+ <h1>Inside the Invocation Construction Kit</h1>
34
+ <div id="version" class="clickable" onclick='document.location = "http://rubyforge.org/projects/ick"; return false'>
35
+ <p>Get Version</p>
36
+ <a href="http://rubyforge.org/projects/ick" class="numbers">0.2.4</a>
37
+ </div>
38
+ <h2>More about the four block structures</h2>
39
+
40
+
41
+ <p><a href="index.html">Ick</a> provides #let, a method for block-structuring Ruby code. As already shown, if you want someone&#8217;s phone number only if they are a friend: <code>let(Person.find(:first, ...)) { |person| person.phone_number if person.friend? }</code></p>
42
+
43
+
44
+ <p>This code makes it clear that you only need the <code>person</code> variable inside the block. If you want to refactor this code, you know that the entire expression can move without breaking another piece of code.</p>
45
+
46
+
47
+ <h3>#let, #returning, #my, and #inside</h3>
48
+
49
+
50
+ <p>As shown above, #let does the obvious thing: it passes a value to a block, it evaluates the block in the caller&#8217;s environment, and it returns the result of the block. Hmmm&#8230;</p>
51
+
52
+
53
+ <p>There are two binary decisions to be made about every block: First, do you want to evaluate the block in the calling environment (which is how almost every block is evaluated in Ruby), or do you want to evaluate the block in the value&#8217;s context. In other words, does <em>self</em> stay the same, or does it become the value in the block?</p>
54
+
55
+
56
+ <p>If you want something that behaves like #let but evaluates in the value&#8217;s environment just like #please, you can use #my:</p>
57
+
58
+
59
+ <p><pre class='syntax'>
60
+ <span class="ident">my</span><span class="punct">(</span><span class="constant">Person</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="keyword">do</span>
61
+ <span class="ident">first_name</span> <span class="punct">=</span> <span class="punct">'</span><span class="string">Charles</span><span class="punct">'</span>
62
+ <span class="ident">last_name</span> <span class="punct">=</span> <span class="punct">'</span><span class="string">Babbage</span><span class="punct">'</span>
63
+ <span class="ident">friends</span> <span class="punct">&lt;&lt;</span> <span class="punct">'</span><span class="string">Ada Lovelace</span><span class="punct">'</span>
64
+ <span class="keyword">end</span>
65
+ </pre></p>
66
+
67
+
68
+ <p>This will return Charles Babbage&#8217;s friends. On the surface, <em>evaluates_in_value_environment</em> is about the syntactic sugar of dropping an instance variable. But with a little thought, you can come up with some really cool way to (mis)use this capability.</p>
69
+
70
+
71
+ <p>So #let and #my both pass an expression to a block and return the result. Given that they both &#8216;declare&#8217; <em>returns_result</em>, this is not surprising. But there is another choice: <em>returns_value</em> instead of <em>returns_result</em>. Ruby on Rails includes the popular #returning method, and it works the same in Ick:</p>
72
+
73
+
74
+ <p><pre class='syntax'>
75
+ <span class="ident">returning</span><span class="punct">(</span><span class="constant">Person</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="keyword">do</span> <span class="punct">|</span><span class="ident">p</span><span class="punct">|</span>
76
+ <span class="ident">p</span><span class="punct">.</span><span class="ident">first_name</span> <span class="punct">=</span> <span class="punct">'</span><span class="string">Charles</span><span class="punct">'</span>
77
+ <span class="ident">p</span><span class="punct">.</span><span class="ident">last_name</span> <span class="punct">=</span> <span class="punct">'</span><span class="string">Babbage</span><span class="punct">'</span>
78
+ <span class="ident">p</span><span class="punct">.</span><span class="ident">friends</span> <span class="punct">&lt;&lt;</span> <span class="punct">'</span><span class="string">Ada Lovelace</span><span class="punct">'</span>
79
+ <span class="keyword">end</span>
80
+ </pre></p>
81
+
82
+
83
+ <p>This returns the person record, not the list of friends. The block is evaluated strictly for side effects. And what happens if we want to return the value and also evaluate in the value&#8217;s environment?</p>
84
+
85
+
86
+ <p><pre class='syntax'>
87
+ <span class="ident">inside</span><span class="punct">(</span><span class="constant">Person</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="keyword">do</span>
88
+ <span class="ident">first_name</span> <span class="punct">=</span> <span class="punct">'</span><span class="string">Charles</span><span class="punct">'</span>
89
+ <span class="ident">last_name</span> <span class="punct">=</span> <span class="punct">'</span><span class="string">Babbage</span><span class="punct">'</span>
90
+ <span class="ident">friends</span> <span class="punct">&lt;&lt;</span> <span class="punct">'</span><span class="string">Ada Lovelace</span><span class="punct">'</span>
91
+ <span class="keyword">end</span>
92
+ </pre></p>
93
+
94
+
95
+ <p>The method #inside returns the value and evaluates the block in the value&#8217;s environment.</p>
96
+
97
+
98
+ <p>(The four methods were inspired by <a href="http://blog.rubyenrails.nl/articles/2008/02/18/our-daily-method-10-object-r-rs-ds-s">Michiel de Mare&#8217;s</a> post on the same subject, although Ick’s nomenclature is not compatible with Michiel’s. Michiel’s #rsss, #rrss, #rsds, and #rrds are called #returning, #let, #inside, and #my in Ick.)</p>
99
+
100
+
101
+ <h3>What about #try and #maybe?</h3>
102
+
103
+
104
+ <p>The methods #try and #maybe are both implemented as <em>evaluates_in_calling_environment</em>, because that is least surprising. But when you&#8217;re rolling your own, you might want to change that to make things more sugary. For example, here is a different version of #try:</p>
105
+
106
+
107
+ <p><pre class='syntax'>
108
+ <span class="keyword">class </span><span class="class">Please</span> <span class="punct">&lt;</span> <span class="constant">Ick</span><span class="punct">::</span><span class="constant">Guard</span>
109
+ <span class="ident">guard_with</span> <span class="punct">{</span> <span class="punct">|</span><span class="ident">value</span><span class="punct">,</span> <span class="ident">sym</span><span class="punct">|</span> <span class="ident">value</span><span class="punct">.</span><span class="ident">respond_to?</span><span class="punct">(</span><span class="ident">sym</span><span class="punct">)</span> <span class="punct">==</span> <span class="constant">true</span> <span class="punct">}</span>
110
+ <span class="ident">evaluates_in_value_environment</span> <span class="keyword">and</span> <span class="ident">returns_result</span>
111
+ <span class="ident">belongs_to</span> <span class="constant">Object</span>
112
+ <span class="keyword">end</span>
113
+
114
+ <span class="ident">please</span><span class="punct">(...)</span> <span class="punct">{</span> <span class="ident">may</span><span class="punct">.</span><span class="ident">i</span><span class="punct">.</span><span class="ident">have</span><span class="punct">.</span><span class="ident">some</span><span class="punct">.</span><span class="ident">more</span> <span class="punct">}</span>
115
+ </pre></p>
116
+
117
+
118
+ <p>The method #please executes in the value&#8217;s environment, and thus it can call methods directly. Here is the key point about Ick as compared to rolling your own methods directly: you can combine and recombine the parts to make new kinds of methods. As you just saw, we can make #try and #please out of the same building blocks. This is why Ick is a &#8220;contruction kit,&#8221; not just a collection of handy syntactic short cuts.</p>
119
+
120
+
121
+ <h2>Wrap Music</h2>
122
+
123
+
124
+ <p>The four methods #let, #returning, #my, and #inside are fairly simple. There&#8217;s some finagling with what they return and their evaluation environment, but evaluation within the environment is 100% standard Ruby. But things get really interesting when we want to actually change Ruby&#8217;s evaluation behaviour. Specifically, when we want to change what it means to call a method on the value we provide.</p>
125
+
126
+
127
+ <p>That&#8217;s what #maybe, #please, and #try all do: when a method is called on the value, they intercept it and decide whether to call the method or prematurely return <em>nil</em>. Ick can&#8217;t actually change the behaviour of the Ruby interpreter, so what it does is as close to evil metaprogramming as possible. Ick doesn&#8217;t do any method redefinition or method chain manipulation, instead it performs its juju by wrapping the value in a delegator. Ick&#8217;s delegators are called <em>Wrappers</em>.</p>
128
+
129
+
130
+ <p>Ick&#8217;s built-in methods that use wrappers inherit from <code>Ick::Wrap</code>. When the result is returned from the block, it is unwrapped if it is wrapped.</p>
131
+
132
+
133
+ <h3>Hand Rolling</h3>
134
+
135
+
136
+ <p>You can use Ick::Wrap along with your own wrappers by calling #invoke_wrapped:</p>
137
+
138
+
139
+ <p><pre class='syntax'>
140
+ class MyWrapper &lt; Ick::Wrapper
141
+ def initialize(value)
142
+ # ...
143
+ end
144
+ # ...
145
+ end
146
+
147
+ Ick::Wrap.instance.invoke_wrapped(Person.find(:first, ...), MyWrapper)
148
+ </pre></p>
149
+
150
+
151
+ <p>If you need additional parameters for your wrapper, you can declare them in the initializer and pass them to #invoke_wrapped:</p>
152
+
153
+
154
+ <p><pre class='syntax'>
155
+ class MyWrapper &lt; Ick::Wrapper
156
+ def initialize(value, extra1, extra2)
157
+ # ...
158
+ end
159
+ # ...
160
+ end
161
+
162
+ Ick::Wrap.instance.invoke_wrapped(Person.find(:first, ...), MyWrapper, :foo, :bar)
163
+ </pre></p>
164
+
165
+
166
+ <p>The methods #maybe, #please, and #try are all implemented as subclasses of Ick:Guard. Have a look at guard.rb to see how to hide the extra wrapping syntax that #invoke_wrapped requires.</p>
167
+
168
+
169
+ <h3>ArrayWrapper</h3>
170
+
171
+
172
+ <p>Ick::ArrayWrapper wraps a collection of receivers. Any message sent to the wrapper is dispatched to each of the receivers in the collection. Here&#8217;s a simplified implementation:</p>
173
+
174
+
175
+ <p><pre class='syntax'>
176
+ <span class="keyword">class </span><span class="class">ArrayWrapper</span> <span class="punct">&lt;</span> <span class="constant">Wrapper</span>
177
+
178
+ <span class="keyword">def </span><span class="method">__invoke__</span><span class="punct">(</span><span class="ident">sym</span><span class="punct">,</span> <span class="punct">*</span><span class="ident">args</span><span class="punct">,</span> <span class="punct">&amp;</span><span class="ident">block</span><span class="punct">)</span>
179
+ <span class="attribute">@value</span><span class="punct">.</span><span class="ident">map</span> <span class="punct">{</span> <span class="punct">|</span><span class="ident">_</span><span class="punct">|</span> <span class="ident">_</span><span class="punct">.</span><span class="ident">__send__</span><span class="punct">(</span><span class="ident">sym</span><span class="punct">,</span> <span class="punct">*</span><span class="ident">args</span><span class="punct">,</span> <span class="punct">&amp;</span><span class="ident">block</span><span class="punct">)</span> <span class="punct">}</span>
180
+ <span class="keyword">end</span>
181
+
182
+ <span class="ident">is_contagious</span>
183
+
184
+ <span class="keyword">end</span>
185
+ </pre></p>
186
+
187
+
188
+ <p>What on Earth is it for? Specifically, what&#8217;s wrong with just using Enumerable#map if we want to send the same message(s) to each receiver in a collection?</p>
189
+
190
+
191
+ <p>The answer is that when we use Enumerable#map, it executes the entire block once for each receiver. So if there are side effects in the block, they are executed once for each receiver as well. Whereas ArrayWrapper dispatches the messages to each receiver without executing side effects more than once.</p>
192
+
193
+
194
+ <p>Ick::Tee provides a method for using an ArrayWrapper with <em>return_value</em> semantics. When you use #tee, it evaluates the block once, but dispatches methods to each of the values you pass in. It returns the first value you pass in. This can be handy for things like sending logging information to more than one log.</p>
195
+
196
+
197
+ <p>Here&#8217;s one of the test cases you can find in the gem:</p>
198
+
199
+
200
+ <p><pre class='syntax'>
201
+ <span class="keyword">def </span><span class="method">test_tee_semantics</span>
202
+ <span class="ident">array_logger_one</span> <span class="punct">=</span> <span class="punct">[]</span>
203
+ <span class="ident">array_logger_two</span> <span class="punct">=</span> <span class="punct">[]</span>
204
+ <span class="ident">line_number</span> <span class="punct">=</span> <span class="number">0</span>
205
+ <span class="punct">[</span><span class="ident">array_logger_one</span><span class="punct">,</span> <span class="ident">array_logger_two</span><span class="punct">].</span><span class="ident">map</span> <span class="keyword">do</span> <span class="punct">|</span><span class="ident">log</span><span class="punct">|</span>
206
+ <span class="ident">line_number</span> <span class="punct">+=</span> <span class="number">1</span>
207
+ <span class="ident">log</span> <span class="punct">&lt;&lt;</span> <span class="punct">&quot;</span><span class="string">A<span class="expr">#{line_number}</span></span><span class="punct">&quot;</span>
208
+ <span class="ident">line_number</span> <span class="punct">+=</span> <span class="number">1</span>
209
+ <span class="ident">log</span> <span class="punct">&lt;&lt;</span> <span class="punct">&quot;</span><span class="string">B<span class="expr">#{line_number}</span></span><span class="punct">&quot;</span>
210
+ <span class="keyword">end</span>
211
+ <span class="ident">assert_equal</span><span class="punct">(%w(</span><span class="string">A1 B2</span><span class="punct">),</span> <span class="ident">array_logger_one</span><span class="punct">)</span>
212
+ <span class="ident">assert_equal</span><span class="punct">(%w(</span><span class="string">A3 B4</span><span class="punct">),</span> <span class="ident">array_logger_two</span><span class="punct">)</span>
213
+ <span class="ident">array_logger_one</span> <span class="punct">=</span> <span class="punct">[]</span>
214
+ <span class="ident">array_logger_two</span> <span class="punct">=</span> <span class="punct">[]</span>
215
+ <span class="ident">line_number</span> <span class="punct">=</span> <span class="number">0</span>
216
+ <span class="ident">tee</span><span class="punct">(</span><span class="ident">array_logger_one</span><span class="punct">,</span> <span class="ident">array_logger_two</span><span class="punct">)</span> <span class="keyword">do</span> <span class="punct">|</span><span class="ident">log</span><span class="punct">|</span>
217
+ <span class="ident">line_number</span> <span class="punct">+=</span> <span class="number">1</span>
218
+ <span class="ident">log</span> <span class="punct">&lt;&lt;</span> <span class="punct">&quot;</span><span class="string">A<span class="expr">#{line_number}</span></span><span class="punct">&quot;</span>
219
+ <span class="ident">line_number</span> <span class="punct">+=</span> <span class="number">1</span>
220
+ <span class="ident">log</span> <span class="punct">&lt;&lt;</span> <span class="punct">&quot;</span><span class="string">B<span class="expr">#{line_number}</span></span><span class="punct">&quot;</span>
221
+ <span class="keyword">end</span>
222
+ <span class="ident">assert_equal</span><span class="punct">(%w(</span><span class="string">A1 B2</span><span class="punct">),</span> <span class="ident">array_logger_one</span><span class="punct">)</span>
223
+ <span class="ident">assert_equal</span><span class="punct">(%w(</span><span class="string">A1 B2</span><span class="punct">),</span> <span class="ident">array_logger_two</span><span class="punct">)</span>
224
+ <span class="keyword">end</span>
225
+ </pre></p>
226
+
227
+
228
+ <p>When we used Enumerable#map, the side effect <code>line_number += 1</code> is evaluated four times, twice for each receiver. However when we use #tee, the side effect <code>line_number += 1</code> is only evaluated twice, even though we have two receivers. Now that you know about the four execution possibilities encapsulated by #let, #returning, #my, and #inside, you can probably guess what #fork does when I tell you that its implementation <em>returns_result</em>.</p>
229
+
230
+
231
+ <h3>Beware!</h3>
232
+
233
+
234
+ <p>Because Ick::Wrap is passing a wrapper into the block instead of the original value, you can get unexpected results under certain circumstances. For example, if you assign the value something else within the block such as an instance variable, when the block exits the value will still have whatever behaviour the wrapper creates.</p>
235
+
236
+
237
+ <p>Therefore, wrappers are <em>unidirectional</em>: they only work when you are calling methods on your value, not when you are passing the wrapped value as a parameter to any other method. This is hideous: why should there be any difference between <code>{ |value| value + 1 }</code> and <code>{ |value| 1 + value }</code>?</p>
238
+
239
+
240
+ <p>Also, wrappers are <em>external</em>, they only affect messages sent to the wrapped value in your block. For example, if you are using #try to avoid NoMethodErrors, any messages you send to the wrapped value will handled by the wrapper. But what happens if the wrapped value sends itself a message that it doesn&#8217;t handle? Aha, that will raise a NoMethodError!</p>
241
+
242
+
243
+ <p>Wrappers are a very leaky abstraction, which is why I try to name things a little closer to what they actually do than what they purport to do but don&#8217;t really do. For example, we write <code>try { something.or.other }</code> to emphasize that we are only trying the things you see in front of you.</p>
244
+
245
+
246
+ <h2>Why Ick?</h2>
247
+
248
+
249
+ <p>As just mentioned, Ick is a construction kit. By all means install the gem and go wild with #let, #returning, #my, #inside, #try, and #maybe. But have a look under the hood. Ick uses classes and template methods to replicate what can be done in a few lines of explicit code. For example, Object#returning is implemented in Rails as:</p>
250
+
251
+
252
+ <p><pre class='syntax'>
253
+ <span class="keyword">class </span><span class="class">Object</span>
254
+ <span class="keyword">def </span><span class="method">returning</span><span class="punct">(</span><span class="ident">value</span><span class="punct">)</span>
255
+ <span class="keyword">yield</span><span class="punct">(</span><span class="ident">value</span><span class="punct">)</span>
256
+ <span class="ident">value</span>
257
+ <span class="keyword">end</span>
258
+ <span class="keyword">end</span>
259
+ </pre></p>
260
+
261
+
262
+ <p>So why bother with Ick? Well, if you want to make a method just like Object#returning, only <em>X</em> (for some value of X), you can&#8217;t do that without copying, pasting, and modifying. Ick&#8217;s classes are included specifically so you can subclass things and make your own new kinds of methods that are variations of the existing methods.</p>
263
+
264
+
265
+ <p>Thus, the extra abstraction is appropriate if you want to use the built-in methods as a starting point for your own exploratory programming. And if you don&#8217;t care, you just want the methods, by all means install the gem and just use them. Don&#8217;t worry about the implementation unless you identify it as a performance problem.</p>
266
+
267
+
268
+ <h3>Where do you want to go today?</h3>
269
+
270
+
271
+ <p>The point behind abstracting invocation and evaluation is that you can <em>separate concerns</em>. For example, which methods to chain is one concern. How to handle nil or an object that does not respond to a method is a separate concern. Should you raise and handle and exception? Return nil? log an error? Why should error handling and logging be intermingled with your code?</p>
272
+
273
+
274
+ <p>With Ick, you can separate the two issues. You can even make the handling pluggable. For example, if instead of calling #let you call your own method, you could sometimes invoke <code>Ick::Let</code> with a block and other times invoke your own handler, perhaps one that logs every method called.</p>
275
+
276
+
277
+ <p>Have fun!</p>
278
+
279
+
280
+ <h2>Administrivia</h2>
281
+
282
+
283
+ <h3>Home Sweet Home</h3>
284
+
285
+
286
+ <p><a href="http://ick.rubyforge.org">ick.rubyforge.org</a></p>
287
+
288
+
289
+ <h3>How to submit patches</h3>
290
+
291
+
292
+ <p>Read the <a href="http://drnicwilliams.com/2007/06/01/8-steps-for-fixing-other-peoples-code/">8 steps for fixing other people&#8217;s code</a>.</p>
293
+
294
+
295
+ <p>The trunk repository is <code>svn://rubyforge.org/var/svn/ick/trunk</code> for anonymous access.</p>
296
+
297
+
298
+ <h3>License</h3>
299
+
300
+
301
+ <p>This code is free to use under the terms of the <a href="http://en.wikipedia.org/wiki/MIT_License"><span class="caps">MIT</span> license</a>.</p>
302
+
303
+
304
+ <h3>Shout Out</h3>
305
+
306
+
307
+ <p><a href="http://mcommons.com/">Mobile Commons</a>. Still Huge After All These Years.</p>
308
+
309
+
310
+ <h3>Contact</h3>
311
+
312
+
313
+ <p>Comments are welcome. Send an email to <a href="mailto:raganwald+rubyforge@gmail.com">Reginald Braithwaite</a>. And you can always visit <a href="http://weblog.raganwald.com/">weblog.raganwald.com</a> to see what&#8217;s cooking.</p>
314
+ <p class="coda">
315
+ <a href="http://weblog.raganwald.com/">Reginald Braithwaite</a>, 9th March 2008<br>
316
+ Theme extended from <a href="http://rb2js.rubyforge.org/">Paul Battley</a>
317
+ </p>
318
+ </div>
319
+
320
+ <!-- insert site tracking codes here, like Google Urchin -->
321
+
322
+ <script type="text/javascript" src="http://pub44.bravenet.com/counter/code.php?id=404724&usernum=3754613835&cpv=2"></script>
323
+
324
+ </body>
325
+ </html>