Nick Sieger: Category rails http://blog.nicksieger.com/articles/category/rails en-us 40 Rails 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&#8217;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&#8217;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 &#8220;access to Access&#8221; 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&#8217;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&#8217;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&#8217;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 &#8220;rpdf&#8221; template handler. Nowadays, you may as well use <a href="http://rubyforge.org/projects/railspdfplugin/">Josh Charles&#8217; Rails PDF plugin</a>, but for posterity I&#8217;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&#8217;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&#8217;d expect:</p> <div class="typocode"><pre><code class="typocode_ruby "><span class="keyword">class </span><span class="class">AddressController</span> <span class="punct">&lt;</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">=&gt;</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">=&gt;</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&#8217;d out.</p> <div class="typocode"><pre><code class="typocode_ruby "><span class="constant">FONT</span> <span class="punct">=</span> <span class="punct">&quot;</span><span class="string">Times-Roman</span><span class="punct">&quot;</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 &amp; 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">&quot;</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">&quot;,</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">&gt;</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&#8217;s it! Avery labels in Rails!</p> Fri, 26 May 2006 03:32:00 +0000 urn:uuid:3d364af1-4743-44e4-bbe9-248c750675ef Nick Sieger http://blog.nicksieger.com/articles/2006/05/26/rails-is-simpler-than-office ruby rails ruby rubyonrails pdf http://blog.nicksieger.com/articles/trackback/17 RailsConf 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&#8217;s <a href="http://facebook.railsconf.org/users/234">me</a>, hope to see you this weekend!</p> Fri, 23 Jun 2006 14:10:00 +0000 urn:uuid:824696e4-63b3-4812-b0bc-cb58ccec0574 Nick Sieger http://blog.nicksieger.com/articles/2006/06/23/railsconf-begins ruby rails railsconf http://blog.nicksieger.com/articles/trackback/21 RailsConf: 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&#8217;s famous &#8220;23 unsolved problems&#8221;&#8230;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 &#8220;disagree mildly&#8221; 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 &#8220;belongs_to&#8221; 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&#8217;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&#8217;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&#8217;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&#8217;m inclined to think that this will complicate things. Do we want Rails view to turn into Wicket widgets or ASP.NET components? It&#8217;s a completely different way of writing web programs. We&#8217;ll have to see how it plays out.</p> <h3>Deployment</h3> <p>Dave argues for improved deployment. Yes we have Capistrano, and it&#8217;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&#8217;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 &lt;name&gt; --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&#8217;s make their lives easier!</p> Fri, 23 Jun 2006 15:12:00 +0000 urn:uuid:1fd545f6-99b1-46e5-b8ae-50093b28f067 Nick Sieger http://blog.nicksieger.com/articles/2006/06/23/railsconf-dave-thomas-keynote ruby rails railsconf http://blog.nicksieger.com/articles/trackback/22 RailsConf: Mike Clark -- Introduction to Capistrano <h2>Introduction to Capistrano</h2> <p>Mike Clark has been Java-free for 15 months and 16 days. He&#8217;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&#8217;s a tribute to him.</p> <p>Capistrano is about making it easy to deploy web applications. &#8220;Cap&#8221;, the short name has quickly become jargon in the Rails community. &#8220;Stop wasting time, just cap it!&#8221;</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&#8217;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">&quot;</span><span class="string">depot</span><span class="punct">&quot;</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">&quot;</span><span class="string">http://svn.yourhost.com/<span class="expr">#{application}</span>/trunk</span><span class="punct">&quot;</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">&quot;</span><span class="string">app01.example.com</span><span class="punct">&quot;,</span> <span class="punct">&quot;</span><span class="string">app02.example.com</span><span class="punct">&quot;</span> <span class="ident">role</span> <span class="symbol">:web</span><span class="punct">,</span> <span class="punct">&quot;</span><span class="string">web01.example.com</span><span class="punct">&quot;,</span> <span class="punct">&quot;</span><span class="string">web02.example.com</span><span class="punct">&quot;</span> <span class="ident">role</span> <span class="symbol">:db</span><span class="punct">,</span> <span class="punct">&quot;</span><span class="string">db.example.com</span><span class="punct">&quot;,</span> <span class="symbol">:primary</span> <span class="punct">=&gt;</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">&quot;</span><span class="string">/Library/Rails/<span class="expr">#{application}</span></span><span class="punct">&quot;</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&#8217;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 &#8220;current&#8221; 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 &#8220;current&#8221; 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&#8217;re in firefighting mode and you don&#8217;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">&quot;</span><span class="string">locate ruby</span><span class="punct">&quot;</span> <span class="ident">task</span> <span class="symbol">:which_ruby</span><span class="punct">,</span> <span class="symbol">:roles</span> <span class="punct">=&gt;</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">&quot;</span><span class="string">You're running:</span><span class="punct">&quot;</span> <span class="ident">run</span> <span class="punct">&quot;</span><span class="string">which ruby</span><span class="punct">&quot;</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">&quot;</span><span class="string">echo Current rev is <span class="expr">#{revision}</span></span><span class="punct">&quot;</span> <span class="ident">run</span> <span class="punct">&quot;</span><span class="string">echo Current rev is at <span class="expr">#{releast_path}</span></span><span class="punct">&quot;</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">=&gt;</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">&quot;</span><span class="string">uptime</span><span class="punct">&quot;</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&#8217;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">=&gt;</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">=&gt;</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">&quot;</span><span class="string">path/to/database.yml</span><span class="punct">&quot;</span> <span class="ident">run</span> <span class="punct">&quot;</span><span class="string">cp <span class="expr">#{production_config}</span> <span class="expr">#{current_path}</span>/config/database.yml</span><span class="punct">&quot;</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">&quot;</span><span class="string">production</span><span class="punct">&quot;</span> <span class="ident">set</span> <span class="symbol">:user</span><span class="punct">,</span> <span class="punct">&quot;</span><span class="string">ops_gal</span><span class="punct">&quot;</span> <span class="keyword">else</span> <span class="ident">set</span> <span class="symbol">:user</span><span class="punct">,</span> <span class="punct">&quot;</span><span class="string">dev_guy</span><span class="punct">&quot;</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&#8217;t just for Rails</h3> <p>It&#8217;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 &#8211; 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&#8217;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&#8217;ve seen is to check out once and rsync everywhere else. I haven&#8217;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&#8217;t. Instead, publish your extensions, blog them, and see if it takes.</em></li> <li><code>deploy_with_migrations</code> &#8211; use with caution as it&#8217;s not as easy to rollback, and not all migrations can be rolled back cleanly.</li> </ul> Fri, 23 Jun 2006 16:45:00 +0000 urn:uuid:fd4b197e-ec51-47aa-bc0c-7e90a4339f19 Nick Sieger http://blog.nicksieger.com/articles/2006/06/23/railsconf-mike-clark-capistrano ruby rails railsconf http://blog.nicksieger.com/articles/trackback/23 RailsConf: 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 &#8211; how fast</li> <li>Throughput &#8211; how many</li> <li>Utilization &#8211; how idle is the cpu</li> <li>Cost efficiency &#8211; 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&#8217;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 &#8211; 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 &#8211; if you server crashes&#8230;oops. Also doesn&#8217;t scale.</li> <li>File system &#8211; easy to set up, scales with NFS, but slower than&#8230;</li> <li>ActiveRecordStore &#8211; easy to set up since it comes with Rails, but much slower than&#8230;</li> <li>SQLSessionStore &#8211; 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 &#8211; slightly faster than SQLSessionStore, scales best, but setup is also more involved.</li> <li>DrbStore &#8211; distributed ruby store</li> </ul> <h2>Caching</h2> <ul> <li>Full pages &#8211; fastest, complete pages are served on the filesystem. Web server bypasses appserver for rendering. If you have private pages, you can&#8217;t use it.</li> <li>Actions &#8211; pages are cached after an action is rendered. The user ID can be used as part of the storage key.</li> <li>Fragments &#8211; fragments can be cached in memory, on the file system, in a DrbStore, or in memcached. Memcached scales the best but doesn&#8217;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&#8217;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 &#8211; don&#8217;t use the inflector if you don&#8217;t need to, it&#8217;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 &#8211; 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 &#8211; don&#8217;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&#8217;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 &#8220;compile&#8221; templates.</li> <li>The idea is to cache results of some ERb scriptlets and essentially &#8220;compile&#8221; 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 &#8211; Stefan replied that it would certainly solve GC issues, but he doesn&#8217;t know if it&#8217;s in a state to be able to benchmark Rails requests.</li> <li>What are your recommendations for a web server. <em>I don&#8217;t have any.</em></li> <li>Is horizontal or vertical scaling better? <em>I don&#8217;t know, I&#8217;ve been focused on making single requests go fast, so I don&#8217;t have enough experience.</em></li> </ul> Fri, 23 Jun 2006 20:40:00 +0000 urn:uuid:032b3183-e579-4896-a439-2f62ee8279e6 Nick Sieger http://blog.nicksieger.com/articles/2006/06/23/railsconf-stefan-kaes-performance ruby rails railsconf http://blog.nicksieger.com/articles/trackback/24 RailsConf: Amy Hoy - Overcoming Scaffolding Addiction <p>Scaffolding is not a swiss army knife, it&#8217;s more like a rusty spoon. It&#8217;ll give you tetanus! Scaffolding leads to brain atrophy and laziness. Leads to bad code, bad UI, etc. Instead, start from scratch. <code>script/generate</code> is not inherently bad, it can write necessary, tedious code for you.</p> <pre><code>script/generate controller kitten create view put_to_sleep </code></pre> <p>This gives you:</p> <div class="typocode"><pre><code class="typocode_ruby "><span class="keyword">class </span><span class="class">KittenController</span> <span class="punct">&lt;</span> <span class="constant">ApplicationController</span> <span class="keyword">def </span><span class="method">create</span> <span class="keyword">end</span> <span class="keyword">def </span><span class="method">view</span> <span class="keyword">end</span> <span class="keyword">def </span><span class="method">put_to_sleep</span> <span class="keyword">end</span> <span class="keyword">end</span></code></pre></div> <p>Amy proceeds to break down a from-scratch, minimalist set of views, to underscore the point that it&#8217;s not too hard to not use scaffolding. I&#8217;d agree, it was so straightforward that unfortunately I&#8217;m going to omit the details here. Rolling your own without scaffolding is highly recommended.</p> <p>Some of the items that you generally will experience in real world apps but do not come in scaffolding are widgets for maintaining relationships. Select lists based on a <code>belongs_to</code>, checkbox groups for <code>has_many</code> are two examples.</p> <p>Amy breaks these down as well and it turns out they&#8217;re pretty easy to do. One snippet of code for updating checkboxes was succinct and surprisingly readable, and made judicious use of the ruby array&#8217;s <code>-</code> behavior to add newly checked and remove newly unchecked items quickly.</p> <p>Amy promised to post snippets and slides later on her blog, so go over to <a href="http://www.slash7.com/">slash7</a> for more.</p> Fri, 23 Jun 2006 21:43:00 +0000 urn:uuid:c75b0ef7-57f2-4856-9713-776cdef0bf23 Nick Sieger http://blog.nicksieger.com/articles/2006/06/23/railsconf-amy-hoy-scaffolding ruby rails railsconf http://blog.nicksieger.com/articles/trackback/25 RailsConf: Obie Fernandez - Thoughtworks on Rails <p>Is anyone doing &#8220;real&#8221; projects in Rails? Obie&#8217;s talk is the answer to this frequently received question.</p> <p>Twix, the first Rails project at Thoughtworks was an internal tool that functioned as an introduction to the technology and began the evangelization process for Rails within Thoughtworks.</p> <p>Apparently DHH and Roy (the owner of Thoughtworks) hit it off when DHH said to Roy in a meeting, &#8220;I want to change the world.&#8221;</p> <p>The Deere project started as a supply-chain application built on Java that took 6 weeks to get up and running. Rails was sneaked in as a tool to help business owners generate sample data for the main application. The interface was simple, and made it fun for the users. It generated quite a stir, with one of the customers exclaiming that it was better than the app they already had!</p> <p>This led to Deere COA (Certificate of Analysis). There was a sticky-note, user-centered design, task analysis session that helped to drive what form the application would take. The resulting implementation took about two calendar weeks, eight person weeks. Obie&#8217;s comment at this point was that the amount of time was on the order how long it usually took the client just to decide that they wanted to do something.</p> <p>Later, as maintenance of the tool was being handed off to some internal developers, Obie gave a brief presentation on Rails to some of the client stakeholders. When he returned to finish training on the technology three months later, he found that a product manager (somewhat non-techie) had built a time tracking application during his two-week family leave that subsequently received a lot of attention at the office. A project manager was asking him to help fix a bug with the application that had already been deployed and in use for three months, and the company wanted to roll it out for use to several thousand people at the company.</p> <p>The story about the Kiosk project highlighted that agile and XP methods fit so well with Rails. Obie described one case where they brought their usability expert in for a review and Rails allowed them to make changes to the UIs as fast as the usability person could point them out.</p> <p><em>Note: Q &amp; A is paraphrased.</em></p> <ul> <li>How can you transform a client&#8217;s thinking about technology and acceptance of Ruby and Rails so that when you leave, they own it? <em>Find a common thread of command-line, text-based development, or experience with similar languages. Most skilled programmers can pick up Rails in days or weeks. Your miles may vary but it goes with the amount of talent you&#8217;re working with.</em></li> <li>Comment on RadRails. <em>Code completion is overrated, it keeps you from solving the problems in your head because the editor is feeding alternatives to you.</em></li> <li>Comment on deployment. <em>The reason you can&#8217;t do Ruby on Rails is that it&#8217;s not a supported technology in our data center. Politics, status quo, not important, blah, blah. We ended up doing the app anyway, and the data center people said, &#8220;sure you can do it, we&#8217;ll give you a VPS&#8221;.</em></li> </ul> Sat, 24 Jun 2006 15:15:00 +0000 urn:uuid:9a1601d8-2595-469d-81f3-56d9fa0c961d Nick Sieger http://blog.nicksieger.com/articles/2006/06/24/railsconf-obie-fernandez ruby rails railsconf http://blog.nicksieger.com/articles/trackback/26 RailsConf: Steven Hammond - DSLs and Rails Plugins <h2>Intro</h2> <p>Steven talks on the whats and whys of domain specific languages.</p> <p>Steven&#8217;s background is is as a java developer and more recently, a project manager, or &#8220;sith lord&#8221; as he put it.</p> <p>Some early exposure to DSLs for him involved the use of something called Program Composition Notation that was used to describe a weather modeling program to be run on massively-parallel supercomputers.</p> <h2>Features of DSLs</h2> <ul> <li>Usually a partial programming language focused on a single problem domain.</li> <li>Replicate syntax/structure that is common to users of that domain.</li> <li>Tries to be as elegant, concise and expressive as possible in that domain.</li> </ul> <h2>Horizontal vs. Vertical</h2> <ul> <li>Horizontal &#8211; programming problems (e.g., Rails migrations)</li> <li>Vertical &#8211; business domain problems (e.g., a game DSL, insurance product definition, circuit board specification)</li> </ul> <h2>Recipe example</h2> <p>Steve contrasted a procedural, C-style recipe vs. a Ruby-based DSL that used blocks to delineate ingredients from procedures. The differentiating point was &#8220;which one could your mom translate into meatloaf&#8221;? I suppose neither if you don&#8217;t like your mom&#8217;s meatloaf.</p> <h2>Rails DSLs</h2> <ul> <li>The framework itself (&#8220;is the whale shark a fish?&#8221;)</li> <li>Migrations</li> <li>RJS templates</li> <li>Plugins <ul> <li><code>acts_as_state_machine</code></li> <li>RESTful Rails</li> <li><code>game_dsl</code></li> </ul></li> </ul> <h2>When to create a DSL</h2> <ul> <li>established syntax or vertical domain</li> <li>soemthing that&#8217;s inelegant in normal imperative or object-oriented style</li> <li>abstract implementation details so that you don&#8217;t depend on them (e.g., migrations does this for database DDL)</li> <li>reuse</li> </ul> <h2>DSLs and Reuse</h2> <ul> <li>&#8220;Reuse is vastly overrated&#8221; &#8211; DHH in 1/2006 <ul> <li>Business logic is very application specific and is difficult to reuse across projects</li> <li>Level of reuse is at a lower lever where there is an information barrier to understanding the component (e.g., LINPACK)</li> </ul></li> </ul> <h2>Designing a DSL</h2> <ul> <li>Be careful to balance forward-looking with extraction. In other words, DSLs should be extracted or driven by need just like anything else.</li> <li>Layout a domain syntax (not necessarily in Ruby)</li> <li>Extract and evolve over time</li> <li>Translate business requirements/statements into succinct programming fragments. (e.g., rolling dice and assigning attributes)</li> </ul> <div class="typocode"><pre><code class="typocode_ruby "><span class="comment"># extending Integer to deal with die-rolling</span> <span class="number">3</span><span class="punct">.</span><span class="ident">d6</span> <span class="number">3</span><span class="punct">.</span><span class="ident">d6</span> <span class="symbol">:keep_best</span> <span class="punct">=&gt;</span> <span class="number">3</span> <span class="comment"># Deck-dealing example</span> <span class="keyword">class </span><span class="class">Tile</span> <span class="punct">&lt;</span> <span class="constant">AciveRecord</span><span class="punct">::</span><span class="constant">Base</span> <span class="ident">acts_as_deck</span> <span class="keyword">end</span> <span class="keyword">class </span><span class="class">Player</span> <span class="punct">&lt;</span> <span class="constant">ActiveRecord</span><span class="punct">::</span><span class="constant">Base</span> <span class="ident">acts_as_player</span> <span class="keyword">end</span> <span class="keyword">class </span><span class="class">DeckController</span> <span class="punct">&lt;</span> <span class="constant">ApplicationController</span> <span class="keyword">def </span><span class="method">deal_cards</span> <span class="attribute">@deck</span> <span class="punct">=</span> <span class="constant">Tile</span><span class="punct">.</span><span class="ident">find</span> <span class="symbol">:all</span> <span class="attribute">@deck</span><span class="punct">.</span><span class="ident">shuffle</span> <span class="attribute">@card</span> <span class="punct">=</span> <span class="attribute">@deck</span><span class="punct">.</span><span class="ident">draw</span> <span class="attribute">@deck</span><span class="punct">.</span><span class="ident">deal</span> <span class="symbol">:cards</span> <span class="punct">=&gt;</span> <span class="number">6</span><span class="punct">,</span> <span class="symbol">:to</span> <span class="punct">=&gt;</span> <span class="attribute">@players</span> <span class="keyword">end</span> <span class="keyword">end</span></code></pre></div> <p>There was a discussion at this point about whether it was necessary to use a DSL in this case, and Obie Fernandez piped in that if you&#8217;re aiming for reuse or as an API, DSLs give a readable, understandable representation that makes reuse and sharing easier.</p> <p>Another comment was regarding namespace collisions, and Steven replied honestly that there is not a good answer. If you combine a game-playing DSL with a home-building DSL, are you randomizing your porch?</p> <p>YAML provides another good medium for succinct data layout (e.g., database.yml). Steven&#8217;s example followed the same pattern to apply to selecting different sources for a random number generator.</p> <h2>Pitfalls</h2> <ul> <li>DSLs should not necessarily be the first solution &#8211; the domain could be too narrow or too broad.</li> <li>Too many DSLs (or even plugin bloat) could end up compromising the readability of the code. Namespacing comes in here. Which DSL/plugin provided the given functionality?</li> <li>Interaction side-effects &#8211; it&#8217;s not clear how mixing metaphors can make the solution clearer.</li> <li>Vertical DSLs additionally suffer from the problem that it&#8217;s still programming, and there are syntax issues to overcome, even though the DSL can faciliate interactions with the customer.</li> </ul> <p>A comment came from Aslak Hellesoy about the use of external DSLs (i.e., built using parser generators). Steven replied that it is appropriate in some cases, but the complexity will be higher.</p> <p>It seems to me that in the case of external DSLs, the freedom (lack of constraints) can actually be a burden. You can design syntax however you want but you need to be sure that it makes sense to the people who will be using it. Unless you&#8217;re really good with language and interface design it can be easy to create a custom, niche language that nobody knows anything about and is inflexible. In my experience it&#8217;s hard to use parser generators like yacc to build flexible parsers. Ruby turns out to be really good for DSLs because you can omit details (parentheses and hash curly braces) and it still does what you want.</p> Sat, 24 Jun 2006 16:18:00 +0000 urn:uuid:9bc768d6-878b-45b8-ab9c-995a570ee47a Nick Sieger http://blog.nicksieger.com/articles/2006/06/24/railsconf-steven-hammond-dsls ruby rails railsconf http://blog.nicksieger.com/articles/trackback/27 RailsConf: Bill Katz - Metaprogramming Writertopia <p>Bill Katz took a code-focused approach to explaining metaprogramming and DSLs.</p> <h2>Resources</h2> <ul> <li><a href="http://www.writertopia.com/developers">http://www.writertopia.com/developers</a> has an authorization plugin used in writertopia, which is the main focus of this talk.</li> <li><a href="http://www.simbiome.org">http://www.simbiome.org</a> and <a href="https://wimtk.org/svn/simbiome">https://wimtk.org/svn/simbiome</a> contain code for physics based simulation resources.</li> </ul> <p>Bill applies Paul Graham&#8217;s quote on bottom-up design and lisp as justification for using Ruby to program DSLs.</p> <h2>Authorization plugin</h2> <p>In search of a better authorization scheme, Bill wanted an elegant syntax using blocks to delineate sections of code that require specific permissions, and a more intuitive way of managing the data surrounding access controls.</p> <div class="typocode"><pre><code class="typocode_ruby "><span class="ident">current_user</span><span class="punct">.</span><span class="ident">has_role?</span> <span class="punct">&quot;</span><span class="string">moderator</span><span class="punct">&quot;</span> <span class="ident">current_user</span><span class="punct">.</span><span class="ident">has_role?</span> <span class="punct">&quot;</span><span class="string">member</span><span class="punct">&quot;,</span> <span class="ident">workshop</span> <span class="ident">workshop</span><span class="punct">.</span><span class="ident">accepts_role?</span> <span class="punct">&quot;</span><span class="string">member</span><span class="punct">&quot;,</span> <span class="ident">user</span> <span class="ident">current_user</span><span class="punct">.</span><span class="ident">is_moderator_of</span> <span class="ident">workshop</span> <span class="ident">user</span><span class="punct">.</span><span class="ident">is_eligible_for?</span> <span class="ident">campbell_award</span></code></pre></div> <h2>Implementing <code>permit</code></h2> <div class="typocode"><pre><code class="typocode_ruby "><span class="keyword">class </span><span class="class">MeetingController</span> <span class="punct">&lt;</span> <span class="constant">ApplicationController</span> <span class="ident">permit</span> <span class="punct">&quot;</span><span class="string">rubyists and wanna_be_rubyists</span><span class="punct">&quot;,</span> <span class="symbol">:except</span> <span class="punct">=&gt;</span> <span class="symbol">:public_text</span></code></pre></div> <p>We need a class method. Where to put it? How does this work? (Insert pretty diagram of instance, singleton class and superclass here.) Metaclassees are &#8220;anonymous&#8221;, you don&#8217;t see them by looking at class ancestors.</p> <div class="typocode"><pre><code class="typocode_ruby "><span class="constant">ActionController</span><span class="punct">::</span><span class="constant">Base</span><span class="punct">.</span><span class="ident">send</span><span class="punct">(</span><span class="symbol">:include</span><span class="punct">,</span> <span class="constant">Authorization</span><span class="punct">::</span><span class="constant">Base</span><span class="punct">)</span> <span class="constant">ActionView</span><span class="punct">::</span><span class="constant">Base</span><span class="punct">.</span><span class="ident">send</span><span class="punct">(</span><span class="symbol">:include</span><span class="punct">,</span> <span class="constant">Authorization</span><span class="punct">::</span><span class="constant">Base</span><span class="punct">::</span><span class="constant">ControllerInstanceMethods</span><span class="punct">)</span></code></pre></div> <p>Singleton methods from the point of view of the object are the same as instance methods in the metaclass. See slides on &#8220;How do we permit?&#8221; for details. Bill chose to tuck the DSL-related methods into modules and used <code>self.included</code>, <code>class_eval</code> and other goodies to set up the methods in the correct places. There is more than way to do this but I suspect Bill chose to do it for purposes of cleanly separating concerns along the lines of where in the class hierarchy the methods live.</p> <div class="typocode"><pre><code class="typocode_ruby "><span class="constant">ActiveRecord</span><span class="punct">::</span><span class="constant">Base</span><span class="punct">.</span><span class="ident">send</span><span class="punct">(</span><span class="symbol">:include</span><span class="punct">,</span> <span class="constant">Authorization</span><span class="punct">::</span><span class="constant">ObjectRolesTable</span><span class="punct">::</span><span class="constant">UserExtensions</span><span class="punct">,</span> <span class="constant">Authorization</span><span class="punct">::</span><span class="constant">ObjectRolesTable</span><span class="punct">::</span><span class="constant">ModelExtensions</span><span class="punct">)</span></code></pre></div> <p>This loads an <code>acts_as_authorizable</code> plugin which, when used in a model, inserts an <code>accepts_role</code> family of methods for querying and setting user roles.</p> <p>Bill blazed through a whole lot of pretty-looking code here for how to set up the DSL infrastructure. You&#8217;ll have to go over to the writertopia developer link to see it. Bill has said he&#8217;ll post his slides online as well.</p> <h2>Questions</h2> <ul> <li>Use of <code>method_missing</code> &#8211; does that clash with <code>ActiveRecord::Base</code> usage of <code>method_missing</code>? <em>No, as long as you use <code>super</code> to invoke the next method in the chain, you&#8217;re ok.</em> Ruby module inclusion semantics handle this for you.</li> <li>How to insert the class methods? A discussion started around <code>ActionController::Base.send(:include, ...)</code> and There is More Than One Way to Do It.</li> </ul> <div class="typocode"><pre><code class="typocode_ruby "><span class="constant">ActionController</span><span class="punct">::</span><span class="constant">Base</span><span class="punct">.</span><span class="ident">send</span><span class="punct">(</span><span class="symbol">:include</span><span class="punct">,</span> <span class="constant">Authorization</span><span class="punct">::</span><span class="constant">Base</span><span class="punct">)</span> <span class="keyword">class </span><span class="class">ActionController::Base</span><span class="punct">;</span> <span class="ident">include</span> <span class="constant">Authorization</span><span class="punct">::</span><span class="constant">Base</span><span class="punct">;</span> <span class="keyword">end</span> <span class="constant">ActionController</span><span class="punct">::</span><span class="constant">Base</span><span class="punct">.</span><span class="ident">class_eval</span> <span class="punct">{</span> <span class="ident">include</span> <span class="constant">Authorization</span><span class="punct">::</span><span class="constant">Base</span> <span class="punct">}</span></code></pre></div> <ul> <li>Question about the class inheritance chain. There are actually two inheritance chains, one for the classes and one for the metaclasses. Resolution at method invocation time travels first to the object&#8217;s metaclass, then to the object&#8217;s class, then to the superclass of the singleton class, then the class&#8217;s superclass, etc. Blech, that makes no sense. Instead, go read <a href="http://whytheluckystiff.net/articles/seeingMetaclassesClearly.html">Seeing Metaclasses Early</a>.</li> </ul> Sat, 24 Jun 2006 17:27:00 +0000 urn:uuid:beeab649-29ff-4113-b389-bf4514295d5f Nick Sieger http://blog.nicksieger.com/articles/2006/06/24/railsconf-bill-katz-metaprogramming ruby rails railsconf http://blog.nicksieger.com/articles/trackback/28 RailsConf: Ezra Zygmuntowicz - Rails Deployment <p><a href="http://brainspl.at/">Ezra</a>: Programming in Ruby for 3 years, Rails for 2. Just another Ruby hacker, he says. <em>Right!</em></p> <h2>Yakima Herald</h2> <ul> <li><a href="http://yakimaherald.com">http://yakimaherald.com</a> &#8211; Local newspaper website</li> <li>CMS</li> <li>Lots of non-SQL content</li> <li>Classified and obituary workflow</li> <li>Internal credit card transaction</li> </ul> <h2>Data Aggregation</h2> <ul> <li>Mysql</li> <li>XML feeds from AP</li> <li>RSS and Atom feeds from parent newspaper</li> <li>Proprietary database for articles <ul> <li>Yucky Vendorscript</li> <li>Crusty OS 9 server!</li> <li>Wrote DSL to emit script over a socket to remote machine</li> </ul></li> </ul> <h2>Deployment</h2> <ul> <li>Apache? Seems to be the industry standard&#8230;</li> </ul> <div class="typocode"><pre><code class="typocode_ruby "><span class="keyword">class </span><span class="class">ApacheWithFCGI</span> <span class="punct">&lt;</span> <span class="constant">ActiveRecord</span><span class="punct">::</span><span class="constant">Base</span> <span class="ident">has_many</span> <span class="symbol">:random_500_errors</span> <span class="keyword">end</span></code></pre></div> <ul> <li>Lighttpd <ul> <li>Better FCGI support</li> <li>More stable</li> </ul></li> <li>Switched to standalone, external FCGIs with capistrano spinner/spawner/reaper tasks, and finally got stable.</li> </ul> <h2>Tuning</h2> <ul> <li>Cache, cache, cache</li> <li>Got RAM?</li> <li>Hot spot/profiling/benchmarking</li> <li>Use httperf not ab</li> <li>See Zed Shaw&#8217;s rant on statistics (<a href="http://www.zedshaw.com/blog/programming/programmer_stats.html">Programmers Need To Learn Statistics Or I Will Kill Them All</a>)</li> </ul> <h2>Shared hosting</h2> <ul> <li>Apache with ProxyPass/ProxyPassReverse talking to multiple lighty instances is about as good as it gets.</li> <li>But let&#8217;s face it&#8230;shared hosting + Rails suck.</li> <li>Only as reliable as the worst application on the server</li> <li>Not always good neighbors</li> <li>Resource constraints</li> <li>Free-for-all</li> </ul> <p>Ezra recommends a Xen VPS for Rails deployment. I&#8217;ve followed his advice; this blog is hosted on a Xen VPS at <a href="http://rimuhosting.com">http://rimuhosting.com</a>.</p> <h2>Deployment models</h2> <ul> <li>Starting point, web, app and db on the same server.</li> <li>Next, move database to a separate server. Use the more powerful hardware to the db.</li> <li>Third, split each role to a separate server. But now you need an NFS to serve static and cached contents. Ezra states you can go a long way with this setup</li> </ul> <div class="typocode"><pre><code class="typocode_ruby "><span class="keyword">class </span><span class="class">FutureOfRailsDeployment</span> <span class="punct">&lt;</span> <span class="constant">ActiveRecord</span><span class="punct">::</span><span class="constant">Base</span> <span class="ident">belongs_to</span> <span class="symbol">:mongrel</span> <span class="keyword">end</span></code></pre></div> <h2>Mongrel</h2> <ul> <li>Speaks plain old HTTP, so you can take advantage of existing tools for load balancing, proxying or caching</li> <li>You can use frontends: Pound, Pen, Balance, Haproxy, Lighttpd (although mod_proxy needs to be fixed), or Apache 2.2 mod<em>proxy</em>balancer</li> <li>Mongrel cluster gem allows easy management of multiple mongrel processes. It&#8217;s straightforward to integrate with capistrano too.</li> </ul> <h2>Engine Yard</h2> <p>Ezra describes a new hosting service he&#8217;s been working on that will give you a greatly simplified but powerful and performant hosting environment for your Rails applications, including a web admin console and deployment directly from SVN revisions and tags.</p> <p>Go to <a href="http://engineyard.com/">Engine Yard</a> for a beta signup email now!</p> <h2>BackgroundDrb</h2> <ul> <li>Distributed Ruby server that manages long runnng tasks.</li> <li>Uses a <code>MiddleMan</code> proxy/stub class that allows the Rails process to connect to the background server.</li> <li>Can use it as a worker queue, (e.g. for sending email) or as a distributed memory cache.</li> <li>You can even query for progress on the background workers and push the result all the way to the browser with an ajax progress bar.</li> <li>The code for BackgroundDrb looks really clean! Go to <a href="http://backgroundrb.rubyforge.org/">http://backgroundrb.rubyforge.org/</a> for the nitty gritty.</li> </ul> Sat, 24 Jun 2006 19:18:00 +0000 urn:uuid:8f4d5e50-85e2-40a8-9038-2b69d1b74322 Nick Sieger http://blog.nicksieger.com/articles/2006/06/24/railsconf-ezra-rails-deployment ruby rails railsconf http://blog.nicksieger.com/articles/trackback/29