Nick Sieger: Category rubytag:blog.nicksieger.com,2005:TypoTypo2007-08-31T16:36:30+00:00Nick Siegerurn:uuid:5600581f-3980-43d5-897f-764d25149abb2006-04-23T04:02:00+00:002007-08-31T16:36:30+00:00Tweaking IRB<p>IRB (Interactive Ruby Shell) is one of those tools that a hacker
learning Ruby hopefully discovers right away. It’s an extremely
useful way to learn the language, verify hunches, test assumptions,
and get immediate feedback. IRB promotes learning by doing, which is
the best way of making something stick in your head. (You can even
<a href="http://tryruby.hobix.com/">try an online version of irb</a> without even installing Ruby!)</p>
<p>The first order of business when using IRB is to setup your
preferences. If you haven’t done so already, create the file
<code>~/.irbrc</code> (<code>%USERPROFILE%.irbrc</code> on windows native ruby). <code>.irbrc</code>
is just a regular ruby script where you can run arbitrary ruby code at
the start of your IRB session. Add the following to <code>.irbrc</code>: </p>
<div class="typocode"><pre><code class="typocode_ruby "><span class="ident">require</span> <span class="punct">'</span><span class="string">irb/completion</span><span class="punct">'</span>
<span class="constant">ARGV</span><span class="punct">.</span><span class="ident">concat</span> <span class="punct">[</span> <span class="punct">"</span><span class="string">--readline</span><span class="punct">",</span> <span class="punct">"</span><span class="string">--prompt-mode</span><span class="punct">",</span> <span class="punct">"</span><span class="string">simple</span><span class="punct">"</span> <span class="punct">]</span></code></pre></div>
<p>This sets up usage of <a href="http://tiswww.tis.case.edu/~chet/readline/rltop.html">readline</a> in your session and turns on TAB
completion, making IRB feel as comfortable as regular old <code>bash</code>. Now
you can type <code>Kernel::<TAB></code> and get a list of available methods!
Good.</p>
<p>Next, the thing that you find yourself doing after using IRB for a
while is cutting and pasting code from your console buffer over to
your text editor. Don’t have Ruby’s reflection rules down yet? Not
sure whether to use <code>instance_eval</code> or <code>module_eval</code> when working on
that metaprogramming hack? Working inside Rails’ <code>script/console</code> and
searching for the right ActiveRecord finder options? No matter how
good your terminal program, you probably have to use the mouse to
select text out of it to copy to your text editor, and hackers hate
having to switch from the keyboard to the mouse when in the flow of
programming.</p>
<p>So here’s a technique that will append commands entered in your IRB
session to a file in your home directory (idea from
<a href="http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/58931">ruby-talk:58931</a>). Put the following in your <code>.irbrc</code>:</p>
<div class="typocode"><pre><code class="typocode_ruby "><span class="keyword">module </span><span class="module">Readline</span>
<span class="keyword">module </span><span class="module">History</span>
<span class="constant">LOG</span> <span class="punct">=</span> <span class="punct">"</span><span class="string"><span class="expr">#{ENV['HOME']}</span>/.irb-history</span><span class="punct">"</span>
<span class="keyword">def </span><span class="method">self.write_log</span><span class="punct">(</span><span class="ident">line</span><span class="punct">)</span>
<span class="constant">File</span><span class="punct">.</span><span class="ident">open</span><span class="punct">(</span><span class="constant">LOG</span><span class="punct">,</span> <span class="punct">'</span><span class="string">ab</span><span class="punct">')</span> <span class="punct">{|</span><span class="ident">f</span><span class="punct">|</span> <span class="ident">f</span> <span class="punct"><<</span> <span class="punct">"</span><span class="string"><span class="expr">#{line}</span>
</span><span class="punct">"}</span>
<span class="keyword">end</span>
<span class="keyword">def </span><span class="method">self.start_session_log</span>
<span class="ident">write_log</span><span class="punct">("</span><span class="string">
# session start: <span class="expr">#{Time.now}</span>
</span><span class="punct">")</span>
<span class="ident">at_exit</span> <span class="punct">{</span> <span class="ident">write_log</span><span class="punct">("</span><span class="string">
# session stop: <span class="expr">#{Time.now}</span>
</span><span class="punct">")</span> <span class="punct">}</span>
<span class="keyword">end</span>
<span class="keyword">end</span>
<span class="keyword">alias</span> <span class="symbol">:old_readline</span> <span class="symbol">:readline</span>
<span class="keyword">def </span><span class="method">readline</span><span class="punct">(*</span><span class="ident">args</span><span class="punct">)</span>
<span class="ident">ln</span> <span class="punct">=</span> <span class="ident">old_readline</span><span class="punct">(*</span><span class="ident">args</span><span class="punct">)</span>
<span class="keyword">begin</span>
<span class="constant">History</span><span class="punct">.</span><span class="ident">write_log</span><span class="punct">(</span><span class="ident">ln</span><span class="punct">)</span>
<span class="keyword">rescue</span>
<span class="keyword">end</span>
<span class="ident">ln</span>
<span class="keyword">end</span>
<span class="keyword">end</span>
<span class="constant">Readline</span><span class="punct">::</span><span class="constant">History</span><span class="punct">.</span><span class="ident">start_session_log</span></code></pre></div>
<p>Now every line typed into IRB will immediately be saved into
<code>~/.irb-history</code>. Exercise left to the reader to bind a custom
keystroke and macro to yank the last line out of that file and
automatically paste into your text editor.</p>
<p>Long-time <code>bash</code> users know that the shell maintains a history of
commands across sessions so that you can access commands you typed
yesterday. Wouldn’t it be nice to do this in IRB as well? Wish
granted:</p>
<div class="typocode"><pre><code class="typocode_ruby "><span class="ident">require</span> <span class="punct">'</span><span class="string">irb/ext/save-history</span><span class="punct">'</span>
<span class="constant">IRB</span><span class="punct">.</span><span class="ident">conf</span><span class="punct">[</span><span class="symbol">:SAVE_HISTORY</span><span class="punct">]</span> <span class="punct">=</span> <span class="number">100</span>
<span class="constant">IRB</span><span class="punct">.</span><span class="ident">conf</span><span class="punct">[</span><span class="symbol">:HISTORY_FILE</span><span class="punct">]</span> <span class="punct">=</span> <span class="punct">"</span><span class="string"><span class="expr">#{ENV['HOME']}</span>/.irb-save-history</span><span class="punct">"</span></code></pre></div>
<p>Happy ruby hacking! If you find any more handy IRB tips leave them at
<a href="http://www.rubygarden.org/ruby?Irb/TipsAndTricks">rubygarden</a>, and let me know about them.</p>
<p><em>Footnote: I realize there is duplication and non-DRY happening here
with two copies of your IRB history, but I came across these
techniques at two different times, and the functions they serve seem
different enough to potentially use them both. If you don’t like
that, choose whichever is more appropriate for your needs.</em></p>
<p><em>(Hope this post serves your needs <a href="http://www.unpossible.com/2006/04/20/time-saver-rails-console-reset">Dan</a>.)</em></p>Nick Siegerurn:uuid:7db60b35-a488-4b32-836a-8588259561aa2006-05-02T18:56:00+00:002007-08-31T18:30:29+00:00Virtual Ruby?<p>I had a chance to dabble around with Python a tiny bit today, building the <a href="http://www.ltg.ed.ac.uk/~ht/xsv-status.html">XSV</a> XML schema validator. Got it working fairly easily on my MacBook Pro after <a href="http://trac.caldersphere.net/projects/main/wiki/MacOSXIntel">a minor detour</a>. Later, I needed to build another copy on a shared linux dev box where I didn’t have root access. Now how does this work in Python? Fortunately, the answer was close at hand: <a href="http://peak.telecommunity.com/DevCenter/EasyInstall#creating-a-virtual-python">a virtual python instance</a>. Nice! You can create a replica of the main python install, and add your personal site-packages there. The only thing that gets copied is the python executable plus a bunch of symlinks to the main libraries and packages. After that point all you need to do is put your virtual python executable ahead of the main executable on your PATH.</p>
<p>If I were to install local packages/gems with Ruby, I’d probably either build my own Ruby or maintain a local packages directory and constantly customize RUBYLIB. Seems like a suboptimal solution. Has anyone attempted a virtual-ruby.rb that does the same as virtual-python.py?</p>Nick Siegerurn:uuid:d5cd5f14-65e3-429a-b26a-1bc108ba84e32006-05-05T14:24:00+00:002007-08-31T18:30:28+00:00Hackers who are fathers and _why<p>Spotted on <a href="http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/191493">ruby-talk</a> today was this anecdote on a Ruby
hacker/father and son, and I just had to share here.</p>
<blockquote>
<p>I trotted my 11 year old son over to the Poignant Guide and he
laughed like crazy at the Cartoon Foxes and did a little
programming, but soon got bored. Wait a minute Alex, let’s try one
more thing, Dwe.. Array. Wow, how do I make my R kill big time (He
knows all about cheats). I showed him the character’s values and he
promptly put in a hugh number for the Rabbit’s strength. He was
delighted to see the larger and larger negative life values appear
whenever he attacked. Dad, is this how all my Gameboy programs
work? Yes, son. Hey, dad I want to do 6dof animation too, can you
teach me? Yes son, here’s a few books for you – Computer Graphics,
Foley et al and Physics for Game Developers, Bourg. Not sure he’ll
read them right away, but the whole episode saved me another $49.95
Game Cube cartridge.</p>
</blockquote>Nick Siegerurn:uuid:3d364af1-4743-44e4-bbe9-248c750675ef2006-05-26T03:32:00+00:002007-08-31T18:30:26+00:00Rails is simpler than Office<p>Before my big blog drought at the beginning of the year, I had an
entry queued up talking about some success I’d experienced with Rails.
A lot in the Rails world has progressed since then, but I still think
the story is worth documenting. Also, the code to generate a PDF of
mailing labels may be useful to somebody out there.</p>
<blockquote>
<p>I’ve had some Rails success lately building a home-use mailing list
manager/rolodex application. There are plenty of ways that such a
list could be maintained without resorting to a full web application
framework such as Rails, but what the heck! The mailing list
started life as an MS Access database; after my work computer was
re-imaged I no longer had “access to Access” so it had a temporary
layover in an Excel spreadsheet. Within the past couple of months I
had moved it to a MySQL database as a way to nurture my fledgling
Rails efforts.</p>
<p>Ok, so nothing real special so far, except that in order to print
mailing labels (one of the primary reasons for keeping such a list)
I’d have to export the names to a .csv file and do a mail merge with
Word. Until the most recent mailing.</p>
<p>On a Saturday night I had the brainstorm to use Austin Ziegler’s
<a href="http://ruby-pdf.rubyforge.org/">PDF::Writer</a> library to create a printable PDF directly from
the Rails app, thus skipping the need to go through the mail merge
rigamarole. Only a couple of hours of effort later, I had my
mother-in-law’s Christmas mailing list printed out! Anyone who has
ever done a mail merge with Word knows that clicking a single link
to create the printable versions of the mailing labels is a <em>huge</em>
improvement in usability. And finally, no MS bits were harmed in
the production of this mailing!</p>
</blockquote>
<p>My starting point in building the code to generate PDFs was <a href="http://wiki.rubyonrails.com/rails/pages/HowtoGeneratePDFs">this page
in the Rails wiki</a>. I decided to use the method that
describes installing an “rpdf” template handler. Nowadays, you may as
well use <a href="http://rubyforge.org/projects/railspdfplugin/">Josh Charles’ Rails PDF plugin</a>, but for posterity
I’ve packaged up my effort as a <a href="http://svn.caldersphere.net/svn/main/plugins/pdfrender/">simple plugin</a> as well
(install into an existing Rails application with <code>./script/plugin
install http://svn.caldersphere.net/svn/main/plugins/pdfrender</code>).</p>
<p>With the plugin in place, all that’s necessary is a controller method
to set up the data for the view, and the view code itself. The
controller is as straightforward as you’d expect:</p>
<div class="typocode"><pre><code class="typocode_ruby "><span class="keyword">class </span><span class="class">AddressController</span> <span class="punct"><</span> <span class="constant">ApplicationController</span>
<span class="comment"># ...</span>
<span class="keyword">def </span><span class="method">pdf</span>
<span class="attribute">@addresses</span> <span class="punct">=</span> <span class="constant">Address</span><span class="punct">.</span><span class="ident">find</span><span class="punct">(</span><span class="symbol">:all</span><span class="punct">,</span> <span class="symbol">:order</span> <span class="punct">=></span> <span class="punct">'</span><span class="string">last_name, first_name</span><span class="punct">')</span>
<span class="ident">render</span> <span class="symbol">:layout</span> <span class="punct">=></span> <span class="constant">false</span>
<span class="keyword">end</span>
<span class="keyword">end</span></code></pre></div>
<p>The view code is a little more hairy but with a little thought the
dimensioning and layout code could easily be DRY’d out.</p>
<div class="typocode"><pre><code class="typocode_ruby "><span class="constant">FONT</span> <span class="punct">=</span> <span class="punct">"</span><span class="string">Times-Roman</span><span class="punct">"</span>
<span class="constant">FONT_SIZE</span> <span class="punct">=</span> <span class="number">12</span>
<span class="constant">COLS</span> <span class="punct">=</span> <span class="number">3</span>
<span class="constant">LABELS_PER_PAGE</span> <span class="punct">=</span> <span class="number">30</span>
<span class="constant">LABELS_PER_COL</span> <span class="punct">=</span> <span class="number">10</span>
<span class="comment"># margins: .5in top & bottom, 0.19 in left and right</span>
<span class="comment"># table column widths: 2.63in | 0.13in | 2.63in | 0.13in | 2.63in</span>
<span class="comment"># table rows: 1in height</span>
<span class="constant">MARG_X</span> <span class="punct">=</span> <span class="ident">pdf</span><span class="punct">.</span><span class="ident">in2pts</span> <span class="number">0.19</span>
<span class="constant">MARG_Y</span> <span class="punct">=</span> <span class="ident">pdf</span><span class="punct">.</span><span class="ident">in2pts</span> <span class="number">0.5</span>
<span class="constant">CELL_Y</span> <span class="punct">=</span> <span class="ident">pdf</span><span class="punct">.</span><span class="ident">in2pts</span> <span class="number">1</span>
<span class="constant">CELL_X</span> <span class="punct">=</span> <span class="ident">pdf</span><span class="punct">.</span><span class="ident">in2pts</span> <span class="number">2.63</span>
<span class="constant">COL_PAD_X</span> <span class="punct">=</span> <span class="ident">pdf</span><span class="punct">.</span><span class="ident">in2pts</span> <span class="number">0.19</span>
<span class="constant">COL1_X</span> <span class="punct">=</span> <span class="constant">MARG_X</span>
<span class="constant">COL2_X</span> <span class="punct">=</span> <span class="constant">COL1_X</span> <span class="punct">+</span> <span class="constant">CELL_X</span> <span class="punct">+</span> <span class="constant">COL_PAD_X</span>
<span class="constant">COL3_X</span> <span class="punct">=</span> <span class="constant">COL2_X</span> <span class="punct">+</span> <span class="constant">CELL_X</span> <span class="punct">+</span> <span class="constant">COL_PAD_X</span>
<span class="constant">CELL_PAD_X</span> <span class="punct">=</span> <span class="ident">pdf</span><span class="punct">.</span><span class="ident">in2pts</span> <span class="number">0.13</span>
<span class="constant">CELL_PAD_Y</span> <span class="punct">=</span> <span class="ident">pdf</span><span class="punct">.</span><span class="ident">in2pts</span> <span class="number">0.25</span>
<span class="constant">CELL_LINE_Y</span> <span class="punct">=</span> <span class="constant">FONT_SIZE</span> <span class="punct">+</span> <span class="number">2</span>
<span class="keyword">def </span><span class="method">cell_x</span><span class="punct">(</span><span class="ident">col</span><span class="punct">)</span>
<span class="punct">[</span><span class="constant">COL1_X</span><span class="punct">,</span> <span class="constant">COL2_X</span><span class="punct">,</span> <span class="constant">COL3_X</span><span class="punct">][</span><span class="ident">col</span><span class="punct">]</span> <span class="punct">+</span> <span class="constant">CELL_PAD_X</span>
<span class="keyword">end</span>
<span class="keyword">def </span><span class="method">cell_y</span><span class="punct">(</span><span class="ident">row</span><span class="punct">,</span> <span class="ident">line</span><span class="punct">)</span>
<span class="constant">MARG_Y</span> <span class="punct">+</span> <span class="punct">((</span><span class="constant">LABELS_PER_COL</span> <span class="punct">-</span> <span class="ident">row</span><span class="punct">)</span> <span class="punct">*</span> <span class="constant">CELL_Y</span><span class="punct">)</span> <span class="punct">-</span> <span class="constant">CELL_PAD_Y</span> <span class="punct">-</span> <span class="punct">(</span><span class="ident">line</span> <span class="punct">*</span> <span class="constant">CELL_LINE_Y</span><span class="punct">)</span>
<span class="keyword">end</span>
<span class="keyword">def </span><span class="method">add_label</span><span class="punct">(</span><span class="ident">row</span><span class="punct">,</span> <span class="ident">col</span><span class="punct">,</span> <span class="ident">addr</span><span class="punct">,</span> <span class="ident">pdf</span><span class="punct">)</span>
<span class="keyword">if</span> <span class="ident">addr</span>
<span class="ident">pdf</span><span class="punct">.</span><span class="ident">add_text_wrap</span><span class="punct">(</span><span class="ident">cell_x</span><span class="punct">(</span><span class="ident">col</span><span class="punct">),</span> <span class="ident">cell_y</span><span class="punct">(</span><span class="ident">row</span><span class="punct">,</span> <span class="number">0</span><span class="punct">),</span> <span class="constant">CELL_X</span><span class="punct">,</span> <span class="ident">addr</span><span class="punct">.</span><span class="ident">name</span><span class="punct">,</span> <span class="constant">FONT_SIZE</span><span class="punct">)</span>
<span class="ident">pdf</span><span class="punct">.</span><span class="ident">add_text_wrap</span><span class="punct">(</span><span class="ident">cell_x</span><span class="punct">(</span><span class="ident">col</span><span class="punct">),</span> <span class="ident">cell_y</span><span class="punct">(</span><span class="ident">row</span><span class="punct">,</span> <span class="number">1</span><span class="punct">),</span> <span class="constant">CELL_X</span><span class="punct">,</span> <span class="ident">addr</span><span class="punct">.</span><span class="ident">address</span><span class="punct">,</span> <span class="constant">FONT_SIZE</span><span class="punct">)</span>
<span class="ident">pdf</span><span class="punct">.</span><span class="ident">add_text_wrap</span><span class="punct">(</span><span class="ident">cell_x</span><span class="punct">(</span><span class="ident">col</span><span class="punct">),</span> <span class="ident">cell_y</span><span class="punct">(</span><span class="ident">row</span><span class="punct">,</span> <span class="number">2</span><span class="punct">),</span> <span class="constant">CELL_X</span><span class="punct">,</span> <span class="punct">"</span><span class="string"><span class="expr">#{addr.city}</span>, <span class="expr">#{addr.state}</span> <span class="expr">#{addr.zip}</span></span><span class="punct">",</span> <span class="constant">FONT_SIZE</span><span class="punct">)</span>
<span class="keyword">end</span>
<span class="keyword">end</span>
<span class="ident">pdf</span><span class="punct">.</span><span class="ident">select_font</span><span class="punct">(</span><span class="constant">FONT</span><span class="punct">)</span>
<span class="ident">pages</span> <span class="punct">=</span> <span class="attribute">@addresses</span><span class="punct">.</span><span class="ident">length</span> <span class="punct">/</span> <span class="constant">LABELS_PER_PAGE</span>
<span class="ident">pages</span> <span class="punct">+=</span> <span class="number">1</span> <span class="keyword">if</span> <span class="punct">(</span><span class="attribute">@addresses</span><span class="punct">.</span><span class="ident">length</span> <span class="punct">%</span> <span class="constant">LABELS_PER_PAGE</span><span class="punct">)</span> <span class="punct">></span> <span class="number">0</span>
<span class="number">0</span><span class="punct">.</span><span class="ident">upto</span><span class="punct">(</span><span class="ident">pages</span> <span class="punct">-</span> <span class="number">1</span><span class="punct">)</span> <span class="keyword">do</span> <span class="punct">|</span><span class="ident">page</span><span class="punct">|</span>
<span class="ident">start</span> <span class="punct">=</span> <span class="ident">page</span> <span class="punct">*</span> <span class="constant">LABELS_PER_PAGE</span>
<span class="ident">address_page</span> <span class="punct">=</span> <span class="attribute">@addresses</span><span class="punct">[</span><span class="ident">start</span><span class="punct">..</span><span class="ident">start</span><span class="punct">+</span><span class="constant">LABELS_PER_PAGE</span><span class="punct">]</span>
<span class="number">0</span><span class="punct">.</span><span class="ident">upto</span><span class="punct">(</span><span class="constant">LABELS_PER_COL</span> <span class="punct">-</span> <span class="number">1</span><span class="punct">)</span> <span class="keyword">do</span> <span class="punct">|</span><span class="ident">row</span><span class="punct">|</span>
<span class="ident">add_label</span><span class="punct">(</span><span class="ident">row</span><span class="punct">,</span> <span class="number">0</span><span class="punct">,</span> <span class="ident">address_page</span><span class="punct">[</span><span class="ident">row</span><span class="punct">*</span><span class="constant">COLS</span><span class="punct">],</span> <span class="ident">pdf</span><span class="punct">)</span>
<span class="ident">add_label</span><span class="punct">(</span><span class="ident">row</span><span class="punct">,</span> <span class="number">1</span><span class="punct">,</span> <span class="ident">address_page</span><span class="punct">[</span><span class="ident">row</span><span class="punct">*</span><span class="constant">COLS</span><span class="punct">+</span><span class="number">1</span><span class="punct">],</span> <span class="ident">pdf</span><span class="punct">)</span>
<span class="ident">add_label</span><span class="punct">(</span><span class="ident">row</span><span class="punct">,</span> <span class="number">2</span><span class="punct">,</span> <span class="ident">address_page</span><span class="punct">[</span><span class="ident">row</span><span class="punct">*</span><span class="constant">COLS</span><span class="punct">+</span><span class="number">2</span><span class="punct">],</span> <span class="ident">pdf</span><span class="punct">)</span>
<span class="keyword">end</span>
<span class="ident">pdf</span><span class="punct">.</span><span class="ident">new_page</span> <span class="keyword">unless</span> <span class="ident">page</span> <span class="punct">+</span> <span class="number">1</span> <span class="punct">==</span> <span class="ident">pages</span>
<span class="keyword">end</span></code></pre></div>
<p>And that’s it! Avery labels in Rails!</p>Nick Siegerurn:uuid:7e13f3c6-3b5b-4ec7-bbec-2078f5762f492006-05-30T16:57:00+00:002007-08-31T17:12:14+00:00.irbrc on Windows<p>Having trouble getting IRB to use a .irbrc file on Windows? The following seems to work:</p>
<ol>
<li>Create the .irbrc file in your <code>%USERPROFILE%</code> directory.</li>
<li>Create an environment variable in the System Properties->Advanced Tab->Environment Variables area called <code>HOME</code> and set it to <code>%USERPROFILE%</code>.</li>
</ol>
<p>An alternate approach is to create an environment variable called <code>IRBRC</code> and set it to the full path of the .irbrc file.</p>
<p>Perhaps IRB should be updated to look in <code>%USERPROFILE%</code> on Windows?</p>
<p>Plug: for handy .irbrc contents refer to <a href="/articles/2006/04/23/tweaking-irb">this previous post</a>.</p>Nick Siegerurn:uuid:f0ecb093-9d8e-477e-9e4b-96508c233bd82006-06-06T15:03:00+00:002007-08-31T18:00:35+00:00QOTD<p>Spotted the following <a href="http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/196055">thread</a> on <a href="http://blade.nagaokaut.ac.jp/ruby/ruby-talk/index.shtml">ruby-talk</a>:</p>
<blockquote>
<p><strong>Pat Maddox</strong>: I hate when languages put a condom on my code.<br/>
<strong>Gennady Bystritsky</strong>: What does it make your code, then? ;-)<br/>
<strong>Mat Schaffer</strong>: Pregnant!</p>
</blockquote>Nick Siegerurn:uuid:824696e4-63b3-4812-b0bc-cb58ccec05742006-06-23T14:10:00+00:002007-08-31T16:41:58+00:00RailsConf begins<p>I got lucky with a last minute ticket and found a way to get to the <a href="http://railsconf.org/">First International Rails Conference</a> in Chicago.</p>
<p><img src="http://codefluency.com/files/railsconf/railsconf-attendee.png" alt="Railsconf attendee"/> <img src="http://facebook.railsconf.org/portraits/457/njs-rails-face_small.jpg?1150985751" alt="Nick on Facebook"/></p>
<p>That’s <a href="http://facebook.railsconf.org/users/234">me</a>, hope to see you this weekend!</p>Nick Siegerurn:uuid:1fd545f6-99b1-46e5-b8ae-50093b28f0672006-06-23T15:12:00+00:002007-08-31T16:41:56+00:00RailsConf: Dave Thomas keynote<h3>Dave Thomas, Professional Cassandra</h3>
<h2>Intro</h2>
<p>Dave starts by dissecting a google search and pointing out how the sponsored links sidebar contains a wealth of paid ads for various topics:</p>
<ul>
<li>Hosting</li>
<li>Consulting</li>
<li>Commercial IDEs and tools</li>
<li>Alternatives to RoR!</li>
</ul>
<p>Moving on to rubyforge, Dave examines some download stats, and points out that there have been 536835 downloads of Rails through <code>gem install rails</code></p>
<p>Dave examines some Google trends mapping of ruby on rails vs the following, showing RoR catching up on some and overtaking others:</p>
<ul>
<li>websphere</li>
<li>jboss</li>
<li>tapestry</li>
<li>spring framework</li>
<li>zend</li>
</ul>
<h2>3 Problems for Rails</h2>
<p>Dave proceeds to give his own, tailored for Rails, version of David Hilbert’s famous “23 unsolved problems”…except due to time constraints, can only discuss three.</p>
<h3>PDI</h3>
<p>An acronym, PDI, has emerged on the Rails core team as a way to encourage people to contribute. <em>Please Do Investigate</em></p>
<p>The challenge is to get new features to be proved out in the community before taking it into the core, the Rails core team is only 12 and has limited bandwidth, and that is the idea behind PDI</p>
<p>The three problem areas are:</p>
<ul>
<li>Data integration</li>
<li>CRUD</li>
<li>Deployment</li>
</ul>
<h3>Data integrations</h3>
<p>Dave mentions that he and David “disagree mildly” on the point of using constraints in the database.</p>
<ul>
<li>automatic validation based on schema</li>
<li>work with foreign keys - make it easy to define in a migration</li>
<li>add a “belongs_to” relationship if a foreign key constraint is detected</li>
<li>primary key support, e.g., non-integer keys, particularly in migrations, and composite primary keys</li>
</ul>
<p>The argument here is that real-world applications, and legacy databases today require these features, and Rails needs to be able to have answers to those questions if it wants to be viable in that area.</p>
<p>Dave is asking for distributed transactions now. Should we be pushing Rails into such an enterprisey direction? If you really need XA transactions, shouldn’t you be banished to a java world?</p>
<p>Non-database models would be a fabulous addition to rails. ActiveRecord::Base currently assumes that it’s talking to a sinle relational datastore. If we could integrate multiple disparate data soures, it would make integrations, REST data sources, and mashups much easier. Microsoft is doing this with LINQ today, and if Rails wants to keep up, it will need this.</p>
<h3>Real-world CRUD</h3>
<p>Dave is arguing that we should beef up scaffolding and make it useful. Scaffolding was the star of the initial 10 minute Rails video that started everything, but yet it’s the ugliest web 1.0 interface ever! No AJAX, no automatic, DRY, in-browser validation. But should we, can we really genericize these interfaces? Maybe a scaffolding widget/component system? It can and probably will happen, but at the momet I’m inclined to think that this will complicate things. Do we want Rails view to turn into Wicket widgets or ASP.NET components? It’s a completely different way of writing web programs. We’ll have to see how it plays out.</p>
<h3>Deployment</h3>
<p>Dave argues for improved deployment. Yes we have Capistrano, and it’s arguably the best web deployment mechanism in existence, but the web of knowledge required to deploy a Rails application is too large, varied and unwieldy.</p>
<p>Capistrano is a push model, ideal for small shops where the developers manage and maintain everything, but it’s not the real world. Larger shops have separate development and admin departments. They have stricter requirements for where files go, pre
and post-deployment hooks, roles, passwords, security, etc. Capistrano currently falls down a bit in this environment.</p>
<p>Dave discusses <code>cap --deploy-on</code>: a hypothetical extension to Capistrano that would allow staged deployment to multiple servers, and the possibility to introduce workflow between developers and administrators. What if ISPs had a standardized infrastructure that would allow hobbyists to issue a <code>cap --deploy-on</code> and instantly have their apps running up on TextDrive, DreamHost, etc. To go further, what if we could deploy from rubygems? <code>gem deploy <name> --on cap://my.isp.com</code>. Typo installation would literally be a single command.</p>
<h3>Closing</h3>
<p>All developers need to be happy, so let’s make their lives easier!</p>Nick Siegerurn:uuid:fd4b197e-ec51-47aa-bc0c-7e90a4339f192006-06-23T16:45:00+00:002007-08-31T16:41:56+00:00RailsConf: Mike Clark -- Introduction to Capistrano<h2>Introduction to Capistrano</h2>
<p>Mike Clark has been Java-free for 15 months and 16 days. He’s here to
talk about deployment with Capistrano.</p>
<p>First off, props to Jamis! There are several hundred people in the
room and it’s a tribute to him.</p>
<p>Capistrano is about making it easy to deploy web applications. “Cap”,
the short name has quickly become jargon in the Rails community.
“Stop wasting time, just cap it!”</p>
<p>Capistrano is built to scale. From a single machine all the way up to
clusters, it is intended to make application deployment as simple as
the push of a button. That’s priceless for reducing friction as your
project nears its first production live date, as well as subsequent
maintenance releases.</p>
<h3>Configuring Capistrano - create a recipe</h3>
<h4>Set the application name</h4>
<div class="typocode"><pre><code class="typocode_ruby "><span class="ident">set</span> <span class="symbol">:application</span><span class="punct">,</span> <span class="punct">"</span><span class="string">depot</span><span class="punct">"</span></code></pre></div>
<h4>Set the repository</h4>
<div class="typocode"><pre><code class="typocode_ruby "><span class="ident">set</span> <span class="symbol">:repository</span><span class="punct">,</span> <span class="punct">"</span><span class="string">http://svn.yourhost.com/<span class="expr">#{application}</span>/trunk</span><span class="punct">"</span></code></pre></div>
<h4>Roles</h4>
<div class="typocode"><pre><code class="typocode_ruby "><span class="ident">role</span> <span class="symbol">:app</span><span class="punct">,</span> <span class="punct">"</span><span class="string">app01.example.com</span><span class="punct">",</span> <span class="punct">"</span><span class="string">app02.example.com</span><span class="punct">"</span>
<span class="ident">role</span> <span class="symbol">:web</span><span class="punct">,</span> <span class="punct">"</span><span class="string">web01.example.com</span><span class="punct">",</span> <span class="punct">"</span><span class="string">web02.example.com</span><span class="punct">"</span>
<span class="ident">role</span> <span class="symbol">:db</span><span class="punct">,</span> <span class="punct">"</span><span class="string">db.example.com</span><span class="punct">",</span> <span class="symbol">:primary</span> <span class="punct">=></span> <span class="constant">true</span></code></pre></div>
<h4>Deployment Root</h4>
<div class="typocode"><pre><code class="typocode_ruby "><span class="ident">set</span> <span class="symbol">:deploy_to</span><span class="punct">,</span> <span class="punct">"</span><span class="string">/Library/Rails/<span class="expr">#{application}</span></span><span class="punct">"</span></code></pre></div>
<h3>Setup</h3>
<p>Run <code>cap setup</code> once to setup the deployment directory structure on
all the roles you’ve configured:</p>
<pre><code>depot
`- releases
`- shared
`- log
`- system
</code></pre>
<h3>First time deploy</h3>
<p>Run <code>cap update_code symlink</code>. This checks out the code, adds a
“current” symlink to the code on the remote machine that points to the
timestamped releases directory where the code was checked out. You
can then manually start your web server(s) if you wish.</p>
<h3>New Release</h3>
<p>Run <code>cap deploy</code>. The newly committed code to your repository gets
pulled, a new release created, and it also restarts fcgi processes.</p>
<h3>Rollback release</h3>
<p>Run <code>cap rollback</code>. The “current” symlink gets updated to the previous release,
and the fcgi processes get restarted.</p>
<h3>Scheduled downtime</h3>
<p>Run <code>cap disable_web</code>. A maintenance screen is put up during the
maintenance period. This is great when you’re in firefighting mode
and you don’t want to think, just get the page up there. To enable
again, use <code>cap enable_web</code>.</p>
<h3>Customization</h3>
<p>Here are examples of several useful, real-world tasks. The fourth
task shows aggregation of tasks together.</p>
<div class="typocode"><pre><code class="typocode_ruby "><span class="ident">desc</span> <span class="punct">"</span><span class="string">locate ruby</span><span class="punct">"</span>
<span class="ident">task</span> <span class="symbol">:which_ruby</span><span class="punct">,</span> <span class="symbol">:roles</span> <span class="punct">=></span> <span class="punct">[</span><span class="symbol">:app</span><span class="punct">]</span> <span class="keyword">do</span>
<span class="ident">puts</span> <span class="punct">"</span><span class="string">You're running:</span><span class="punct">"</span>
<span class="ident">run</span> <span class="punct">"</span><span class="string">which ruby</span><span class="punct">"</span>
<span class="keyword">end</span>
<span class="ident">task</span> <span class="symbol">:current_revision</span> <span class="keyword">do</span>
<span class="ident">run</span> <span class="punct">"</span><span class="string">echo Current rev is <span class="expr">#{revision}</span></span><span class="punct">"</span>
<span class="ident">run</span> <span class="punct">"</span><span class="string">echo Current rev is at <span class="expr">#{releast_path}</span></span><span class="punct">"</span>
<span class="keyword">end</span>
<span class="ident">task</span> <span class="symbol">:uptime</span><span class="punct">,</span> <span class="symbol">:roles</span> <span class="punct">=></span> <span class="punct">[</span><span class="symbol">:app</span><span class="punct">,</span> <span class="symbol">:web</span><span class="punct">,</span> <span class="symbol">:db</span><span class="punct">]</span> <span class="keyword">do</span>
<span class="ident">run</span> <span class="punct">"</span><span class="string">uptime</span><span class="punct">"</span>
<span class="keyword">end</span>
<span class="ident">task</span> <span class="symbol">:status</span> <span class="keyword">do</span>
<span class="ident">which_ruby</span>
<span class="ident">current_revision</span>
<span class="ident">uptime</span>
<span class="keyword">end</span></code></pre></div>
<h3>Channels and streams</h3>
<p>Mike showed a log file tailing task here. I failed to capture the full task code, so you’re on your own here.</p>
<h3>Hooks</h3>
<div class="typocode"><pre><code class="typocode_ruby "><span class="ident">task</span> <span class="symbol">:before_deploy</span><span class="punct">,</span> <span class="symbol">:roles</span> <span class="punct">=></span> <span class="punct">[</span><span class="symbol">:app</span><span class="punct">]</span> <span class="keyword">do</span>
<span class="ident">cleanup</span>
<span class="keyword">end</span>
<span class="ident">task</span> <span class="symbol">:after_update_code</span><span class="punct">,</span> <span class="symbol">:roles</span> <span class="punct">=></span> <span class="punct">[</span><span class="symbol">:app</span><span class="punct">]</span> <span class="keyword">do</span>
<span class="ident">production_config</span> <span class="punct">=</span> <span class="punct">"</span><span class="string">path/to/database.yml</span><span class="punct">"</span>
<span class="ident">run</span> <span class="punct">"</span><span class="string">cp <span class="expr">#{production_config}</span> <span class="expr">#{current_path}</span>/config/database.yml</span><span class="punct">"</span>
<span class="keyword">end</span></code></pre></div>
<h3>Multiple configs</h3>
<div class="typocode"><pre><code class="typocode_ruby "><span class="keyword">if</span> <span class="ident">where</span> <span class="punct">==</span> <span class="punct">"</span><span class="string">production</span><span class="punct">"</span>
<span class="ident">set</span> <span class="symbol">:user</span><span class="punct">,</span> <span class="punct">"</span><span class="string">ops_gal</span><span class="punct">"</span>
<span class="keyword">else</span>
<span class="ident">set</span> <span class="symbol">:user</span><span class="punct">,</span> <span class="punct">"</span><span class="string">dev_guy</span><span class="punct">"</span>
<span class="keyword">end</span></code></pre></div>
<h3>Task libraries</h3>
<p>You can creat a file full of extra, reusable tasks and simply
<code>require</code> them in. The files can be published and installed as gems
or placed anywhere on your load path.</p>
<h3>Capistrano isn’t just for Rails</h3>
<p>It’s great at shuttling files around and executing remote commands on
the server. Mike describes some automation James Duncan Davidson did
to replicate new servers into a cluster with the push of a button.</p>
<h3>Assumptions</h3>
<p>Capistrano, out of the box comes with some assumptions:</p>
<ul>
<li>Remote servers talk POSIX</li>
<li>Same deploy structure and password on each machine</li>
<li>Web application uses FastCGI with Apache or Lighty</li>
</ul>
<p>It looks like you can bend some of these assumptions if you are
willing to write your own custom deployment tasks.</p>
<h3>Take Home</h3>
<p>Capistrano provides easy, consistent, worry-free deployment of new
releases. The friction to deploying your app just got simpler, so you
can go enjoy your life!</p>
<h3>Notes and Questions</h3>
<ul>
<li>Can you use SSH public keys to authenticate? <em>Yes, this is the most
typical scenario.</em></li>
<li>You can use <code>sudo</code> to execute remote commands – this is built-in.</li>
<li>Can you/would you want to check out subsets of files on each server,
rather than pulling all the code everywhere? <em>So far most of our
stuff has been on the same server, so this hasn’t been an issue. It
seems like you would need to override deployment tasks for some
roles.</em></li>
<li>Sometimes I want to deploy a lot, but what if the app is big, lots
of media files? It can take a longer time to pull all the
code. <em>One solution I’ve seen is to check out once and rsync
everywhere else. I haven’t had to do that myself.</em></li>
<li>Who can I talk to for promoting my own extensions into Capistrano.
<em>You probably don’t. Instead, publish your extensions, blog
them, and see if it takes.</em></li>
<li><code>deploy_with_migrations</code> – use with caution as it’s not as easy to
rollback, and not all migrations can be rolled back cleanly.</li>
</ul>Nick Siegerurn:uuid:032b3183-e579-4896-a439-2f62ee8279e62006-06-23T20:40:00+00:002007-08-31T16:41:56+00:00RailsConf: Stefan Kaes - Rails Performance<p>Stefan starts by citing a factor of 4-5 improvement in performance in
Rails over the last year.</p>
<h2>Performance, broken down</h2>
<ul>
<li>Latency – how fast</li>
<li>Throughput – how many</li>
<li>Utilization – how idle is the cpu</li>
<li>Cost efficiency – performance per unit cost</li>
</ul>
<p>For completeness calculate the min, max, mean and standard deviation
of these metrics and use the deviation as your guide for how reliable
the data is.</p>
<h2>Tools</h2>
<ul>
<li>Log files (level > <code>Logger::DEBUG</code>)</li>
<li><a href="http://rails-analyzer.rubyforge.org/">Rails Analyzer Tools</a> (Eric Hodel)</li>
<li>Benchmarker (<code>script/benchmarker</code>)</li>
<li>DB vendor tools</li>
<li>Apache bench (<code>ab</code> or <code>ab2</code>)</li>
<li>httperf</li>
<li><a href="http://rubyforge.org/projects/railsbench">railsbench</a> (Stefan Kaes)</li>
</ul>
<h2>Railsbench</h2>
<p>Railsbench measuress raw performance of rails request processing.
It’s configured using <code>config/benchmarks.yml</code> and
<code>config/benchmarks.rb</code>. These files let you control which requests
get benchmarked, whether to create a new session when benchmarking
them, etc.</p>
<h2>Profiling Tools</h2>
<ul>
<li>Ruby Profiler</li>
<li>Zen Profiler</li>
<li>rubyprof</li>
<li>Rails profiler script</li>
<li><a href="http://www.softwareverify.com/rubyPerformanceValidator/index.html">Ruby Performance Validator</a></li>
</ul>
<p>At this point Stefan gave an overview of RPV, which appears to be a
nifty tool that lets you get typical hotspot tree views of where time
is spent in code. It currently only runs on Windows.</p>
<h2>Top Rails Performance Problems</h2>
<ul>
<li>slow helper methods</li>
<li>complicated routes</li>
<li>associations – navigating and eager loading vs. proxy loading</li>
<li>retrieving too much data from the DB</li>
<li>slow session storage (e.g., ActiveRecord store)</li>
</ul>
<p>Stefan says that in his experience, DB performance is generally not a
big factor or bottleneck. Instantiating ActiveRecord objects is
expensive, though.</p>
<h2>Session containers</h2>
<ul>
<li>In memory – if you server crashes…oops. Also doesn’t scale.</li>
<li>File system – easy to set up, scales with NFS, but slower than…</li>
<li>ActiveRecordStore – easy to set up since it comes with Rails, but
much slower than…</li>
<li>SQLSessionStore – which uses the same table structure as
ActiveRecordStore, but was written by Stefan to overcome performance
issues with ActiveRecordStore. Setup is more involved.</li>
<li>memcached – slightly faster than SQLSessionStore, scales best, but
setup is also more involved.</li>
<li>DrbStore – distributed ruby store</li>
</ul>
<h2>Caching</h2>
<ul>
<li>Full pages – fastest, complete pages are served on the
filesystem. Web server bypasses appserver for rendering. If you
have private pages, you can’t use it.</li>
<li>Actions – pages are cached after an action is rendered. The
user ID can be used as part of the storage key.</li>
<li>Fragments – fragments can be cached in memory, on the file system,
in a DrbStore, or in memcached. Memcached scales the best but
doesn’t support expiring fragments by regular expression.</li>
</ul>
<h2>ActionController</h2>
<ul>
<li>Stefan recommends avoiding components, and replacing them with
helpers or partials. He has not found a use for them.</li>
</ul>
<h2>ActionView</h2>
<ul>
<li>Don’t create unnecessary instance variables in the controller;
creating them in the view with <code>instance_variable_set</code> and accessing
with <code>instance_variable_get</code> is slow.</li>
</ul>
<h2>Helpers</h2>
<ul>
<li>pluralize – don’t use the inflector if you don’t need to, it’s expensive.</li>
<li>link<em>to and url</em>for are among the slowest helpers, since they need
to use routes. Instead, if you have page with lots of links, you
might consider hard-coding the links. This reduces the amount of GC
by up to 50% and the GC time down by a few percentage points (11.3%
to 8.7% of total processing time).</li>
</ul>
<h2>ActiveRecord</h2>
<ul>
<li>use the <code>:include</code> option to prefetch associations, it avoids extra
onesy-twosy SQL statements.</li>
<li>use piggy-backing plugin for <code>has_one</code> or <code>belongs_to</code> relationships
– allows you to retrieve extra attributes from additional tables in
the same fetch query.</li>
<li>Field values are retrieved from the DB mostly as strings, so type
conversion happens on each access, which can be slow.</li>
</ul>
<h2>Language-level and miscellaneous issues</h2>
<ul>
<li>Method calls are the slowest – don’t needlessly create method
abstractions</li>
<li>Short-circuit intermediate results to improve performance</li>
<li>Cache results in instance variables or class variables</li>
<li>Don’t call <code>ObjectSpace.each_object</code> on each request</li>
</ul>
<h2>Ruby Memory Management</h2>
<ul>
<li>designed for batch scripts, not long-running servers.</li>
<li>no generational garbage collection.</li>
<li>this is suboptimal for Rails because ASTs are stored on the heap
(biggest portion of non-garbage for Rails apps), and get
processed/traversed more often than they need to be</li>
<li><a href="http://rubyforge.org/projects/railsbench">Railsbench</a> includes a patch to allow one to recompile Ruby
and tweak the garbage collector.</li>
</ul>
<h2>Rails Template Optimizer</h2>
<ul>
<li>Stefan has started a project to “compile” templates.</li>
<li>The idea is to cache results of some ERb scriptlets and essentially
“compile” or replace the template with one that has more expressions
expanded or inlined.</li>
<li>Code forthcoming; I assume you can stay tuned to <a href="http://railsexpress.de/blog/">Rails
Express</a> for news.</li>
</ul>
<h2>Questions</h2>
<ul>
<li>There was a question on JRuby – Stefan replied that it would
certainly solve GC issues, but he doesn’t know if it’s in a state to
be able to benchmark Rails requests.</li>
<li>What are your recommendations for a web server. <em>I don’t have
any.</em></li>
<li>Is horizontal or vertical scaling better? <em>I don’t know, I’ve
been focused on making single requests go fast, so I don’t have
enough experience.</em></li>
</ul>