Gray Soft / Ruby Voodoo / Interpolation and Statementstag:graysoftinc.com,2014-03-20:/posts/572014-04-11T16:52:57ZJames Edward Gray IIThe 7th Comment on "Interpolation and Statements"tag:graysoftinc.com,2008-11-25:/comments/2292014-04-11T16:52:57ZThat's a good point. It's worth noting that `join()` also includes a `to_s()` call for all members:
```ruby
>> [42, nil, Object.new].join(" ")
=> "42 #<Object:0x5303a4>"
```<p>That's a good point. It's worth noting that <code>join()</code> also includes a <code>to_s()</code> call for all members:</p>
<div class="highlight highlight-ruby"><pre><span class="o">>></span> <span class="o">[</span><span class="mi">42</span><span class="p">,</span> <span class="kp">nil</span><span class="p">,</span> <span class="no">Object</span><span class="o">.</span><span class="n">new</span><span class="o">].</span><span class="n">join</span><span class="p">(</span><span class="s2">" "</span><span class="p">)</span>
<span class="o">=></span> <span class="s2">"42 #<Object:0x5303a4>"</span>
</pre></div>James Edward Gray IIThe 6th Comment on "Interpolation and Statements"tag:graysoftinc.com,2008-11-25:/comments/2282014-04-11T16:52:57ZGuilty as charged. I hereby repent and won't use `to_s` in that context anymore.
But how about this one:
```ruby
[first, last].join(' ')
```
I think that looks nicer, especially when there are many elements. You can still call `strip` o...<p>Guilty as charged. I hereby repent and won't use <code>to_s</code> in that context anymore.</p>
<p>But how about this one:</p>
<div class="highlight highlight-ruby"><pre><span class="o">[</span><span class="n">first</span><span class="p">,</span> <span class="n">last</span><span class="o">].</span><span class="n">join</span><span class="p">(</span><span class="s1">' '</span><span class="p">)</span>
</pre></div>
<p>I think that looks nicer, especially when there are many elements. You can still call <code>strip</code> on the result.</p>Geoffrey GrosenbachThe 5th Comment on "Interpolation and Statements"tag:graysoftinc.com,2008-10-10:/comments/2182014-04-11T16:50:59ZI'm nothing if not an amateur, so I might not be the best judge of what 'heaps of programming skills' contain :-)
They're no replacement for loving one's work, though.
For my part, I couldn't tell you precisely what keeps me going. It's a wi...<p>I'm nothing if not an amateur, so I might not be the best judge of what 'heaps of programming skills' contain :-)</p>
<p>They're no replacement for loving one's work, though.</p>
<p>For my part, I couldn't tell you precisely what keeps me going. It's a wide variety of factors. One such is definitely the kick I get when the core of a work in progress works for the first time. In those moments I always feel like laughing loudly and exclaiming 'It's alive! muhahahahaaaa!' :)</p>
<p>It's really insane the number of hours it can take me to get from no code to that moment.</p>
<p>Sometimes I wish I was a street sweeper. At least they have shorter work cycles.</p>HansCzThe 4th Comment on "Interpolation and Statements"tag:graysoftinc.com,2008-10-09:/comments/2172014-04-11T16:50:59ZI am definitely missing the "heaps of programming skills" you credit me with, but you're right on that I love what I do. Can you believe people pay me to play with Ruby (my favorite toy)? It still surprises me.<p>I am definitely missing the "heaps of programming skills" you credit me with, but you're right on that I love what I do. Can you believe people pay me to play with Ruby (my favorite toy)? It still surprises me.</p>James Edward Gray IIThe 3rd Comment on "Interpolation and Statements"tag:graysoftinc.com,2008-10-09:/comments/2162014-04-11T16:50:59ZHey James
When my mother makes these great dishes that nobody else can do quite like her, I have taken to peering over her shoulder in the hopes I can learn to do it like her. Most of the time I'm busy guessing at ingredients and quantities, be...<p>Hey James</p>
<p>When my mother makes these great dishes that nobody else can do quite like her, I have taken to peering over her shoulder in the hopes I can learn to do it like her. Most of the time I'm busy guessing at ingredients and quantities, because she never weighs anything out. She does that by 'feel'. And most importantly: I watch her at work.</p>
<p>I'm guessing your posts and screencasts are made of: </p>
<ul>
<li>at least 5 cups of playful enthusiasm for Ruby </li>
<li>heaps of programming skills</li>
<li>2 handfuls of intuition about pedagogy/teaching</li>
</ul><p>You have a certain tone that says to me: 'I am having fun doing this'.<br>
That makes it a joy to read, watch and learn.</p>
<p>Thank you.</p>
<p>P.S. Despite my hard work, I'm pretty sure you can still smash windows with my homemade meatballs ;-)</p>HansCzThe 2nd Comment on "Interpolation and Statements"tag:graysoftinc.com,2008-10-07:/comments/2092014-04-11T16:13:02ZI'm glad to hear readers are enjoying them. Thanks for the high praise.<p>I'm glad to hear readers are enjoying them. Thanks for the high praise.</p>James Edward Gray IIThe 1st Comment on "Interpolation and Statements"tag:graysoftinc.com,2008-10-07:/comments/2082014-04-11T16:13:02ZI just love these posts! Ruby is still very new to me, but your posts just show how amazingly cool Ruby can be, and you show how you can take great advantage of this coolness.
Thank you!<p>I just love these posts! Ruby is still very new to me, but your posts just show how amazingly cool Ruby can be, and you show how you can take great advantage of this coolness.</p>
<p>Thank you!</p>Peter HazaInterpolation and Statementstag:graysoftinc.com,2008-10-02:/posts/572014-04-11T16:52:57ZThere are two aspects of Ruby syntax that really have a great synergy when used together. Let me show you what I mean.<p>I still cringe anytime I see code like:</p>
<div class="highlight highlight-ruby"><pre><span class="s2">"1 + 2 = "</span> <span class="o">+</span> <span class="p">(</span><span class="mi">1</span> <span class="o">+</span> <span class="mi">2</span><span class="p">)</span><span class="o">.</span><span class="n">to_s</span> <span class="c1"># => "1 + 2 = 3"</span>
</pre></div>
<p>Some books even advocate the above, which is a real shame for Ruby.</p>
<p>I imagine most of you know that you can rewrite the above to use <code>String</code> interpolation:</p>
<div class="highlight highlight-ruby"><pre><span class="s2">"1 + 2 = </span><span class="si">#{</span><span class="mi">1</span> <span class="o">+</span> <span class="mi">2</span><span class="si">}</span><span class="s2">"</span> <span class="c1"># => "1 + 2 = 3"</span>
</pre></div>
<p>Let's think about that simple code a little bit more than we usually do though. What's really going on here? Obviously <code>#{ … }</code> inserts the result of the embedded code in the <code>String</code>, but it's important to realize that it also calls <code>to_s()</code> on that result to make it fit in the <code>String</code>.</p>
<p>We can really make use of that knowledge if we try. Here's an example:</p>
<div class="highlight highlight-ruby"><pre><span class="no">Name</span> <span class="o">=</span> <span class="no">Struct</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="ss">:first</span><span class="p">,</span> <span class="ss">:last</span><span class="p">)</span> <span class="k">do</span>
<span class="k">def</span> <span class="nf">full</span>
<span class="s2">"</span><span class="si">#{</span><span class="n">first</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="n">last</span><span class="si">}</span><span class="s2">"</span><span class="o">.</span><span class="n">strip</span> <span class="c1"># trick 1</span>
<span class="k">end</span>
<span class="n">alias_method</span> <span class="ss">:to_s</span><span class="p">,</span> <span class="ss">:full</span> <span class="c1"># trick 2</span>
<span class="k">end</span>
<span class="no">Name</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">"James"</span><span class="p">)</span><span class="o">.</span><span class="n">full</span> <span class="c1"># => "James"</span>
<span class="no">Name</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="ss">:James</span><span class="p">,</span> <span class="ss">:Gray</span><span class="p">)</span><span class="o">.</span><span class="n">full</span> <span class="c1"># => "James Gray"</span>
<span class="s2">"My name is </span><span class="si">#{</span><span class="no">Name</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s1">'James'</span><span class="p">,</span> <span class="s1">'Gray'</span><span class="p">)</span><span class="si">}</span><span class="s2">."</span> <span class="c1"># => "My name is James Gray."</span>
</pre></div>
<p>I've built a trivial data class for managing names here. In that, I've tried to make use of interpolation to the fullest.</p>
<p>First, I needed to be able to build a reasonable representation of a full name no matter what data I have. My first example shows how this might play out when I only have a first name. That means <code>first()</code> will return <code>"James"</code> and <code>last()</code> will return <code>nil</code>. There are several ways to deal with this, but I chose what I consider to be one of the easiest.</p>
<p>I want something like a <code>String</code> for first and last name. Interpolation pretty much enforces this for me, since it automatically calls <code>to_s()</code> on the interpolated values. It just so happens that <code>nil.to_s</code> is <code>""</code> and following that up with a simple <code>strip()</code> will remove any excess space due to missing names.</p>
<p>You can also see from the second example that I don't have to be strictly using <code>String</code> objects for the names. Anything with a reasonable <code>to_s()</code> will do.</p>
<p>Taking that one step further, the third example shows that even <code>Name</code> itself can benefit from a reasonable <code>to_s()</code>. This allows me to drop the full object right into any old <code>String</code>. You can take this really far by just adding sensible <code>to_s()</code> definitions to all kinds of objects. You could have objects representing data changes dropping themselves right into audit logs, have game move classes automatically serialize themselves for transport over a network protocol, or anything else you can think of.</p>
<p>Hopefully I've made that point now. The implicit <code>to_s()</code> of <code>String</code> interpolation is nice. Now let's look at another aspect of Ruby.</p>
<p>Many languages go to great lengths to distinguish between concepts like a <em>statement</em> and an <em>expression</em>, what each of those does, and where they can appear. Thankfully, Ruby doesn't much care.</p>
<p>What does that mean for us? Well, just about everything evaluates to something. You can kind of think of it as everything having a return value. (It's not really a return value in the case of something like a conditional, but I think that description makes for a good visual of what we are talking about here.) Let me show some examples:</p>
<div class="highlight highlight-ruby"><pre><span class="n">a</span> <span class="o">=</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">2</span>
<span class="n">a</span> <span class="c1"># => 2</span>
<span class="n">b</span> <span class="c1"># => 2</span>
<span class="n">num</span> <span class="o">=</span> <span class="nb">rand</span><span class="p">(</span><span class="mi">20</span><span class="p">)</span> <span class="o">-</span> <span class="mi">10</span>
<span class="n">type</span> <span class="o">=</span> <span class="k">if</span> <span class="n">num</span> <span class="o">>=</span> <span class="mi">0</span> <span class="k">then</span> <span class="ss">:positive</span>
<span class="k">else</span> <span class="ss">:negative</span>
<span class="k">end</span>
<span class="o">[</span><span class="n">num</span><span class="p">,</span> <span class="n">type</span><span class="o">]</span> <span class="c1"># => [-3, :negative]</span>
<span class="n">age</span> <span class="o">=</span> <span class="nb">rand</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span>
<span class="n">group</span> <span class="o">=</span> <span class="k">case</span> <span class="n">age</span>
<span class="k">when</span> <span class="mi">0</span><span class="o">.</span><span class="n">.</span><span class="mi">18</span> <span class="k">then</span> <span class="ss">:child</span>
<span class="k">when</span> <span class="mi">19</span><span class="o">.</span><span class="n">.</span><span class="mi">65</span> <span class="k">then</span> <span class="ss">:adult</span>
<span class="k">when</span> <span class="mi">66</span><span class="o">.</span><span class="n">.</span><span class="mi">100</span> <span class="k">then</span> <span class="ss">:senior</span>
<span class="k">end</span>
<span class="o">[</span><span class="n">age</span><span class="p">,</span> <span class="n">group</span><span class="o">]</span> <span class="c1"># => [44, :adult]</span>
<span class="n">not_run</span> <span class="o">=</span> <span class="k">if</span> <span class="kp">false</span>
<span class="c1"># ...</span>
<span class="k">end</span>
<span class="n">not_run</span> <span class="c1"># => nil</span>
<span class="k">def</span> <span class="nf">do_nothing</span><span class="p">;</span> <span class="k">end</span>
<span class="n">not_run</span> <span class="o">=</span> <span class="n">do_nothing</span>
<span class="n">not_run</span> <span class="c1"># => nil</span>
</pre></div>
<p>Hopefully something in there surprises you. Take special note of the last two which pretty much show that Ruby tends to just go with <code>nil</code> when there's nothing else for a construct to evaluate to. We can use that.</p>
<p>Now, let's throw those two concepts together. Interpolation calls <code>to_s()</code> on the results of any code and we now know that most any code will have a result. That means we can write code like this:</p>
<div class="highlight highlight-ruby"><pre><span class="k">def</span> <span class="nf">pluralize</span><span class="p">(</span><span class="n">count</span><span class="p">,</span> <span class="n">singular</span><span class="p">)</span>
<span class="s2">"</span><span class="si">#{</span><span class="n">count</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="n">singular</span><span class="si">}#{</span><span class="s1">'s'</span> <span class="k">unless</span> <span class="n">count</span> <span class="o">==</span> <span class="mi">1</span><span class="si">}</span><span class="s2">"</span>
<span class="k">end</span>
<span class="mi">3</span><span class="o">.</span><span class="n">times</span> <span class="p">{</span> <span class="o">|</span><span class="n">n</span><span class="o">|</span> <span class="nb">puts</span> <span class="n">pluralize</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="s2">"trick"</span><span class="p">)</span> <span class="p">}</span>
<span class="c1"># >> 0 tricks</span>
<span class="c1"># >> 1 trick</span>
<span class="c1"># >> 2 tricks</span>
</pre></div>
<p>Obviously, this is a pretty dumb method compared to the similar functionality in <code>ActiveSupport</code> and other libraries, but check out that last interpolation. I dumped a full <code>unless</code> right in the <code>String</code>. If it triggers, it will evaluate to a simple <code>'s'</code>, which may be an unusual use of a conditional but is exactly what we need here. When it doesn't trigger, we know Ruby will punt with <code>nil</code> and <code>nil.to_s</code> is nothing, so it won't affect our output at all.</p>
<p>Like anything, you can take this technique too far. If you find yourself embedding entire programs in a <code>String</code> it's time to turn in your keyboard. However, if you find yourself slinging <code>to_s()</code> calls like they are going out of style, there's probably some cleanup you could try.</p>James Edward Gray II