<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom">

  <title>mileszs blahg</title>
  <subtitle>Blahg about programming, mostly</subtitle>
  
  <link href="http://mileszs.com/" />
  <updated>2009-07-08T15:17:08-04:00</updated>
  <author>
    <name>Miles Z. Sterrett</name>
    <email>miles.sterrett@gmail.com</email>
  </author>
  <id>http://mileszs.com/</id>
  
  <link rel="self" href="http://feeds.feedburner.com/mileszs" type="application/atom+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><entry>
    <title>Wicked PDF Plugin</title>
    <link href="/blog/2009/06/17/wicked-pdf-plugin.html" />
    <id>tag:mileszs.com,2009-06-17:1245281591</id>
    <updated>2009-06-17T19:33:11-04:00</updated>
    <content type="html">&lt;p&gt;A bit of background, in the hopes that it will help you save some time:&lt;/p&gt;
&lt;p&gt;At &lt;strong&gt;The Job&lt;/strong&gt;, I &lt;em&gt;need&lt;/em&gt; PDF generation.  We want to generate various documents in PDF form that are professional looking without requiring our users print a particular web page to some PDF printer.  I&amp;#8217;d prefer it be really easy for me to write, of course, but if it&amp;#8217;s something I can do once and never again, I can probably live with a convoluted DSL of some sort.  So, once upon a Google, my search began.&lt;/p&gt;
&lt;p&gt;&amp;#8230; and it was frustrating.&lt;/p&gt;
&lt;h3&gt;Princely&lt;/h3&gt;
&lt;p&gt;I started with &lt;a href="http://github.com/mbleigh/princely/tree/master"&gt;Princely&lt;/a&gt;, a Rails plugin that uses the &lt;a href="http://www.princexml.com/"&gt;PrinceXML library&lt;/a&gt; to generate PDFs from Rails views.  It&amp;#8217;s super-simple.  It&amp;#8217;s &lt;em&gt;almost&lt;/em&gt; everything I wanted in a PDF generation plugin.  It allows you to do all your layout and prettifying with CSS and HTML, which is sweet because &amp;#8212; damn &amp;#8212; I already know HTML and CSS.  Hell yes.&lt;/p&gt;
&lt;p&gt;Except that it watermarks all your PDFs with a blue square containing a white &amp;#8216;P&amp;#8217;.  You can get rid of it if you&amp;#8217;ve got $3800.  Verdict?  &lt;strong&gt;Not worth it&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;Prawn&lt;/h3&gt;
&lt;p&gt;I moved to playing with &lt;a href="http://github.com/sandal/prawn/tree/master"&gt;Prawn&lt;/a&gt;, which is essentially a Ruby-ish DSL for laying out and generating PDFs.  It was a nightmare.  &lt;a href="http://gist.github.com/125953"&gt;Check out this gist&lt;/a&gt; to get an idea of what I mean.  It can&amp;#8217;t be printed here.  It&amp;#8217;s too big and cumbersome.  It&amp;#8217;ll break my blog. &amp;#8230; Here&amp;#8217;s a taste:&lt;/p&gt;
&lt;p&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;
&lt;pre&gt;&lt;span class="no"&gt; 1&lt;/span&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;info_table&lt;/span&gt;&lt;br /&gt;
&lt;span class="no"&gt; 2&lt;/span&gt;     tags &lt;span class="sy"&gt;:blue&lt;/span&gt; =&amp;gt; { &lt;span class="sy"&gt;:color&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;#8216;&lt;/span&gt;&lt;span class="k"&gt;blue&lt;/span&gt;&lt;span class="dl"&gt;&amp;#8217;&lt;/span&gt;&lt;/span&gt; },&lt;br /&gt;
&lt;span class="no"&gt; 3&lt;/span&gt;          &lt;span class="sy"&gt;:green&lt;/span&gt; =&amp;gt; { &lt;span class="sy"&gt;:color&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;#8216;&lt;/span&gt;&lt;span class="k"&gt;green&lt;/span&gt;&lt;span class="dl"&gt;&amp;#8217;&lt;/span&gt;&lt;/span&gt; }&lt;br /&gt;
&lt;span class="no"&gt; 4&lt;/span&gt;  &lt;br /&gt;
&lt;span class="no"&gt; 5&lt;/span&gt;     bounding_box [&lt;span class="i"&gt;235&lt;/span&gt;, &lt;span class="i"&gt;700&lt;/span&gt;], &lt;span class="sy"&gt;:width&lt;/span&gt; =&amp;gt; &lt;span class="i"&gt;300&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;&lt;br /&gt;
&lt;span class="no"&gt; 6&lt;/span&gt;       font &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Times-Roman&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="no"&gt; 7&lt;/span&gt;       fill_color(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;#8216;&lt;/span&gt;&lt;span class="k"&gt;999999&lt;/span&gt;&lt;span class="dl"&gt;&amp;#8217;&lt;/span&gt;&lt;/span&gt;)&lt;br /&gt;
&lt;span class="no"&gt; 8&lt;/span&gt;       text &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;TERM SHEET&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="sy"&gt;:size&lt;/span&gt; =&amp;gt; &lt;span class="i"&gt;24&lt;/span&gt;, &lt;span class="sy"&gt;:align&lt;/span&gt; =&amp;gt; &lt;span class="sy"&gt;:right&lt;/span&gt;&lt;br /&gt;
&lt;span class="no"&gt; 9&lt;/span&gt;       fill_color(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;#8216;&lt;/span&gt;&lt;span class="k"&gt;000000&lt;/span&gt;&lt;span class="dl"&gt;&amp;#8217;&lt;/span&gt;&lt;/span&gt;)&lt;br /&gt;
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;       font &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Helvetica&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="no"&gt;11&lt;/span&gt;       text &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;&amp;lt;blue&amp;gt;Keep IT Safe,&amp;lt;/blue&amp;gt; &amp;lt;green&amp;gt;Keep IT Simple&amp;lt;/green&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="sy"&gt;:size&lt;/span&gt; =&amp;gt; &lt;span class="i"&gt;12&lt;/span&gt;, &lt;span class="sy"&gt;:align&lt;/span&gt; =&amp;gt; &lt;span class="sy"&gt;:right&lt;/span&gt;&lt;br /&gt;
&lt;span class="no"&gt;12&lt;/span&gt;     &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;
&lt;span class="no"&gt;13&lt;/span&gt;     bounding_box [&lt;span class="i"&gt;260&lt;/span&gt;, &lt;span class="i"&gt;650&lt;/span&gt;] &lt;span class="r"&gt;do&lt;/span&gt;&lt;br /&gt;
&lt;span class="no"&gt;14&lt;/span&gt;       table [[&lt;span class="iv"&gt;&lt;code&gt;ticket&amp;lt;/span&amp;gt;, &amp;lt;span class="iv"&amp;gt;&lt;/code&gt;sales_rep&lt;/span&gt;, &lt;span class="iv"&gt;&lt;code&gt;doc_no&amp;lt;/span&amp;gt;, &amp;lt;span class="iv"&amp;gt;&lt;/code&gt;project&lt;/span&gt;, &lt;span class="iv"&gt;@date&lt;/span&gt;]],&lt;br /&gt;
&lt;span class="no"&gt;15&lt;/span&gt;         &lt;span class="sy"&gt;:headers&lt;/span&gt; =&amp;gt; [&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;#8216;&lt;/span&gt;&lt;span class="k"&gt;TICKET&lt;/span&gt;&lt;span class="dl"&gt;&amp;#8217;&lt;/span&gt;&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;#8216;&lt;/span&gt;&lt;span class="k"&gt;SALES REP&lt;/span&gt;&lt;span class="dl"&gt;&amp;#8217;&lt;/span&gt;&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;#8216;&lt;/span&gt;&lt;span class="k"&gt;DOC. NO.&lt;/span&gt;&lt;span class="dl"&gt;&amp;#8217;&lt;/span&gt;&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;#8216;&lt;/span&gt;&lt;span class="k"&gt;PROJECT NO.&lt;/span&gt;&lt;span class="dl"&gt;&amp;#8217;&lt;/span&gt;&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;#8216;&lt;/span&gt;&lt;span class="k"&gt;DATE&lt;/span&gt;&lt;span class="dl"&gt;&amp;#8217;&lt;/span&gt;&lt;/span&gt;],&lt;br /&gt;
&lt;span class="no"&gt;16&lt;/span&gt;         &lt;span class="sy"&gt;:header_color&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;#8216;&lt;/span&gt;&lt;span class="k"&gt;000000&lt;/span&gt;&lt;span class="dl"&gt;&amp;#8217;&lt;/span&gt;&lt;/span&gt;,&lt;br /&gt;
&lt;span class="no"&gt;17&lt;/span&gt;         &lt;span class="sy"&gt;:header_text_color&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;#8216;&lt;/span&gt;&lt;span class="k"&gt;ffffff&lt;/span&gt;&lt;span class="dl"&gt;&amp;#8217;&lt;/span&gt;&lt;/span&gt;,&lt;br /&gt;
&lt;span class="no"&gt;18&lt;/span&gt;         &lt;span class="sy"&gt;:align_headers&lt;/span&gt; =&amp;gt; &lt;span class="sy"&gt;:center&lt;/span&gt;,&lt;br /&gt;
&lt;span class="no"&gt;19&lt;/span&gt;         &lt;span class="sy"&gt;:vertical_padding&lt;/span&gt; =&amp;gt; &lt;span class="i"&gt;2&lt;/span&gt;,&lt;br /&gt;
&lt;span class="no"&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;/span&gt;         &lt;span class="sy"&gt;:horizontal_padding&lt;/span&gt; =&amp;gt; &lt;span class="i"&gt;2&lt;/span&gt;,&lt;br /&gt;
&lt;span class="no"&gt;21&lt;/span&gt;         &lt;span class="sy"&gt;:font_size&lt;/span&gt; =&amp;gt; &lt;span class="i"&gt;8&lt;/span&gt;,&lt;br /&gt;
&lt;span class="no"&gt;22&lt;/span&gt;         &lt;span class="sy"&gt;:width&lt;/span&gt; =&amp;gt; &lt;span class="i"&gt;272&lt;/span&gt;,&lt;br /&gt;
&lt;span class="no"&gt;23&lt;/span&gt;         &lt;span class="sy"&gt;:border_width&lt;/span&gt; =&amp;gt; &lt;span class="fl"&gt;0.2&lt;/span&gt;,&lt;br /&gt;
&lt;span class="no"&gt;24&lt;/span&gt;         &lt;span class="sy"&gt;:align&lt;/span&gt; =&amp;gt; { &lt;span class="i"&gt;0&lt;/span&gt; =&amp;gt; &lt;span class="sy"&gt;:center&lt;/span&gt;, &lt;span class="i"&gt;1&lt;/span&gt; =&amp;gt; &lt;span class="sy"&gt;:center&lt;/span&gt;, &lt;span class="i"&gt;2&lt;/span&gt; =&amp;gt; &lt;span class="sy"&gt;:center&lt;/span&gt;, &lt;span class="i"&gt;3&lt;/span&gt; =&amp;gt; &lt;span class="sy"&gt;:center&lt;/span&gt;, &lt;span class="i"&gt;4&lt;/span&gt; =&amp;gt; &lt;span class="sy"&gt;:center&lt;/span&gt;, &lt;span class="i"&gt;5&lt;/span&gt; =&amp;gt; &lt;span class="sy"&gt;:center&lt;/span&gt;, &lt;span class="i"&gt;6&lt;/span&gt; =&amp;gt; &lt;span class="sy"&gt;:left&lt;/span&gt; }&lt;br /&gt;
&lt;span class="no"&gt;25&lt;/span&gt;     &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;
&lt;span class="no"&gt;26&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;
&lt;/pre&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
I appreciate what the Prawn library can do.  I appreciate all the hard work that has been put into it.  It&amp;#8217;s just not for me.  &lt;strong&gt;So not worth it&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;Wicked PDF&lt;/h3&gt;
&lt;p&gt;So, with a little help from &lt;a href="http://jqr.github.com/"&gt;Elijah Miller&lt;/a&gt; and the &lt;a href="http://indyrb.org"&gt;Indy.rb&lt;/a&gt; IRC channel (#indyrb on Freenode), the shell utility &lt;a href="http://code.google.com/p/wkhtmltopdf/"&gt;wkhtmltopdf&lt;/a&gt; landed in my lap.  It uses the WebKit engine via QT 4.4 to render HTML and CSS and convert it to PDF.  It&amp;#8217;s free.  It&amp;#8217;s exactly what I needed.  Based upon wkhtmltopdf, the &lt;a href="http://github.com/mileszs/wicked_pdf/tree/master"&gt;Wicked PDF plugin&lt;/a&gt; was born to make PDF generation in your Rails app easier and cheaper than it was previously.&lt;/p&gt;
&lt;p&gt;First of all, it&amp;#8217;s &lt;em&gt;wicked&lt;/em&gt;-easy to get started.  After you&amp;#8217;ve visited &lt;a href="http://code.google.com/p/wkhtmltopdf/"&gt;wkhtmltopdf&lt;/a&gt; and installed wkhtmltopdf, simply:&lt;/p&gt;
&lt;p&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;
&lt;pre&gt;script/plugin install git&lt;span class="sy"&gt;:/&lt;/span&gt;/github.com/mileszs/wicked_pdf.git&lt;br /&gt;
&lt;/pre&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
In your controller:&lt;/p&gt;
&lt;p&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;
&lt;pre&gt;&lt;span class="no"&gt; 1&lt;/span&gt; &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;ThingsController&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ApplicationController&lt;/span&gt;&lt;br /&gt;
&lt;span class="no"&gt; 2&lt;/span&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;show&lt;/span&gt;&lt;br /&gt;
&lt;span class="no"&gt; 3&lt;/span&gt;     respond_to &lt;span class="r"&gt;do&lt;/span&gt; |format|&lt;br /&gt;
&lt;span class="no"&gt; 4&lt;/span&gt;       format.html&lt;br /&gt;
&lt;span class="no"&gt; 5&lt;/span&gt;       format.pdf &lt;span class="r"&gt;do&lt;/span&gt;&lt;br /&gt;
&lt;span class="no"&gt; 6&lt;/span&gt;         render &lt;span class="sy"&gt;:pdf&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;file_name&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;,&lt;br /&gt;
&lt;span class="no"&gt; 7&lt;/span&gt;                &lt;span class="sy"&gt;:template&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;things/show.pdf.erb&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;,&lt;br /&gt;
&lt;span class="no"&gt; 8&lt;/span&gt;                &lt;span class="sy"&gt;:layout&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;pdf.html&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="no"&gt; 9&lt;/span&gt;       &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;
&lt;span class="no"&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;     &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;
&lt;span class="no"&gt;11&lt;/span&gt;   &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;
&lt;span class="no"&gt;12&lt;/span&gt; &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;
&lt;/pre&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
You don&amp;#8217;t have to specify a template, as it will choose the template for the current controller and action by default.  Also, by default, layout is set to false, so you may want to keep that around.&lt;/p&gt;
&lt;p&gt;I hope this helps others who have travelled the long, lonely, crufty, potentially expensive route of finding a sufficiently powerful and simple PDF generator.  If you find it lacking, or if you find a bug, please report it to &lt;a href="http://github.com/mileszs/wicked_pdf/issues"&gt;Wicked PDF&amp;#8217;s Issues page&lt;/a&gt; so I can make it do right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Look:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/wicked_witch_speaks.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;You &lt;em&gt;know&lt;/em&gt; the Wicked Witch likes it.&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <title>Vim, Screen, &amp; &lt;br /&gt;The Awesomest IDE Ever</title>
    <link href="/blog/2009/06/05/vim-and-screen.html" />
    <id>tag:mileszs.com,2009-06-05:1244236765</id>
    <updated>2009-06-05T17:19:25-04:00</updated>
    <content type="html">&lt;p&gt;When I&amp;#8217;m writing code, I am always using two classic tools, common on many UNIX-based systems: &lt;a href="http://vim.org"&gt;Vim&lt;/a&gt;, and &lt;a href="http://www.google.com/search?q=GNU+screen"&gt;GNU Screen&lt;/a&gt;.  I&amp;#8217;ll show you how &lt;em&gt;I&lt;/em&gt; use them, as I don&amp;#8217;t think any two programmers use them the same way.  Some programmers may prefer to use a big, fancy &amp;#8216;IDE&amp;#8217;.  There is no need.  Vim and Screen can combine to be &lt;strong&gt;The Awesomest IDE Ever&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;:  My day job primarily consists of writing Ruby for Ruby on Rails applications, so the following outline is focused on that type of environment.  What am I &lt;em&gt;saying&lt;/em&gt;?  Most of the programming I do outside of work involves Ruby or Rails, too.&lt;/p&gt;
&lt;h3&gt;GNU Screen&lt;/h3&gt;
&lt;p&gt;I&amp;#8217;ll start with &lt;a href="http://www.google.com/search?q=GNU+screen"&gt;GNU Screen&lt;/a&gt;, as it&amp;#8217;s the lesser-known of the two subjects of this post.  (I&amp;#8217;ll call it simply &amp;#8216;screen&amp;#8217; from here on &amp;#8212; but if you&amp;#8217;re Googling, you&amp;#8217;ll want to include &amp;#8216;GNU&amp;#8217;.)  Screen is often described as a &amp;#8216;terminal multiplexor&amp;#8217;.  GNU likes to call it a full-screen window manager.  A clearer description would be &amp;#8220;screen takes the concept of tab support in a terminal, and pumps it full of steroids&amp;#8221;.  At its core, screen allows you to create multiple terminal windows within a single terminal &amp;#8212; like, say, Gnome terminal&amp;#8217;s tabs.  It does &lt;em&gt;so&lt;/em&gt; much more than that, though.  From GNU:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;#8230; at any time, you can create new (full-screen) windows with other programs in them (including more shells), kill the current window, view a list of the active windows, turn output logging on and off, copy text between windows, view the scrollback history, switch between windows, etc. All windows run their programs completely independent of each other. Programs continue to run when their window is currently not visible and even when the whole screen session is detached from the users terminal.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The latter feature, detaching from the terminal, is perhaps the most &lt;del&gt;important&lt;/del&gt; awesome feature.  A number of people use screen for that feature alone.  For instance, I often SSH into my home desktop machine and re-attach to a screen session that has a window for IRC, a window with an open SSH session for each of the servers I administer, and a window open for miscellaneous commands I may want or need to run on my home machine.  I can detach from this set of terminal &amp;#8216;windows&amp;#8217; or &amp;#8216;tabs&amp;#8217; at any time, and reattach at any time.  It&amp;#8217;s fantastic if you like to never miss a word of your local Ruby group&amp;#8217;s IRC chatter.  That&amp;#8217;s a completely practical application of that feature.  Right?&lt;/p&gt;
&lt;p&gt;These aren&amp;#8217;t all of the features of screen, but I&amp;#8217;m long-winded without getting worked up about discussing every feature of GNU screen.  You can &lt;em&gt;clearly&lt;/em&gt; see how screen provides the infrastructure for &lt;strong&gt;The Awesomest IDE Ever&lt;/strong&gt;.  Let&amp;#8217;s just move on.&lt;/p&gt;
&lt;h3&gt;Vim&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://vim.org"&gt;Vi IMproved&lt;/a&gt;.  It&amp;#8217;s often called a programmer&amp;#8217;s editor, although I use it for anything and everything, when I can get away with it.  (I used it to write this rambling, incoherant post.  The rambling and the incoherance have nothing to do with Vim, I swear.)  Vim uses different modes (e.g. &amp;#8216;Insert&amp;#8217; mode, &amp;#8216;Visual&amp;#8217; mode, &amp;#8216;Normal&amp;#8217; mode) as a powerful and efficient way to traverse and edit text files.  It&amp;#8217;s highly configurable and extendable.  It&amp;#8217;s great.  If you&amp;#8217;ve never used it, and you think it&amp;#8217;s going to be impossible to learn, it&amp;#8217;s not!  If you have Vim installed, and you want to give it a shot, just type &amp;#8216;vimtutor&amp;#8217; at a command prompt, and follow the tutorial.&lt;/p&gt;
&lt;h3&gt;Vim + Screen&lt;/h3&gt;
&lt;p&gt;By combining Vim + Screen, you can get your own IDE that doesn&amp;#8217;t completely suck.  Resources.  Suck resources.  I&amp;#8217;m not saying that any other &amp;#8216;IDE&amp;#8217; things that I have ever used are each a horribly twisted orgy of unnecessary features and mouse-slam-inducing bugs, possessing the customization possibilities of a straight-jacket.  They aren&amp;#8217;t basically a large pile of naked, over-weight, pasty, inept middle-aged men, necessarily.  No, I&amp;#8217;m just saying that the filthy bastards hog a lot of resources.  Vim, Screen, and the few other processes I run along side them, when working on a Rails app, do not.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s look at a pretty picture:&lt;br /&gt;
&lt;img src="/images/vim_and_screen.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;The two most interesting features are the split screen, and the pretty little window list at the bottom of my terminal window.  Both features are made possible by screen.  Let&amp;#8217;s focus on the window list first:&lt;/p&gt;
&lt;h3&gt;Screen Details&lt;/h3&gt;
&lt;p&gt;&lt;img src="/images/screen_bar.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Sweet.  We&amp;#8217;ve got a nice little list of windows, one for each thing I want out of my development environment, but in a fairly simple, unobtrusive format: &amp;#8216;dev&amp;#8217;, my primary development window, &amp;#8216;test&amp;#8217;, where my tests get run (in this case, using &amp;#8216;autotest&amp;#8217;, they are run any time I save a file), &amp;#8216;console&amp;#8217;, for my Ruby REPL (script/console from Rails), &amp;#8216;server&amp;#8217; for the app server, &amp;#8216;tail&amp;#8217; for printing whatever log I feel like following, and &amp;#8216;misc&amp;#8217; for one-off commands and the like.  I can cycle through the windows by typing Ctrl+a, then &amp;#8216;n&amp;#8217; for next, or &amp;#8216;p&amp;#8217; for previous, or the number of the window.  In just a few pixels of space, I know where everything is, and I don&amp;#8217;t have to grab my mouse to get there.  Suck it, Mammoth IDE Software.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The window list at the bottom doesn&amp;#8217;t come for free.  You&amp;#8217;re welcome to use my configuration file for screen, &amp;#8216;screenrc&amp;#8217;.  &lt;a href="http://github.com/mileszs/dotfiles/blob/5a74d9a18c9061b0a8534efb306ca8348ddb7735/screenrc"&gt;This file&lt;/a&gt; is exactly the screenrc used in this article&amp;#8217;s screenshots.&lt;/p&gt;
&lt;p&gt;As you can see, screen also allows you to split your terminal window so that you can view two (or three, or four &amp;#8212; go crazy!) screen windows at the same time.  For me, this is killer.  I am able to code in Vim while watching my test output in the smaller pane on the bottom.  No pop-up messages to distract you.  No sound effects.  (Hmm.  Perhaps I&amp;#8217;m too easily distracted.  And too easily annoyed.)  It&amp;#8217;s also trivial to focus on the bottom window (Ctrl+a, then j or tab), at which point I can scroll the output, or I can switch to, say, the console window to try some stuff out there before I throw it in the actual code.  Screen can also split vertically, if you dig that.  With a widescreen monitor, why wouldn&amp;#8217;t you?  I prefer to use &lt;em&gt;Vim&amp;#8217;s&lt;/em&gt; vertical split screen ability (in my top pane) to edit, for example, a class and its tests at the same time.  However, It does seem like I&amp;#8217;m wasting a lot of space in that bottom pane.  Let&amp;#8217;s try a vertical split down there:&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/vsplit_screen.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Sweet!&lt;/em&gt;  I&amp;#8217;m glad you talked me into trying that.  That&amp;#8217;s Vim split vertically on top, and screen split vertically on the bottom (tests on the left, REPL on the right).&lt;/p&gt;
&lt;h3&gt;Vim Details&lt;/h3&gt;
&lt;p&gt;You can find fantastic information about Vim all over the place.  I don&amp;#8217;t want to bury you under finger-gymnast technique for both screen &lt;em&gt;and&lt;/em&gt; vim, if I can help it.&lt;/p&gt;
&lt;p&gt;A good place to start look for information is &lt;a href="http://vim.org"&gt;Vim.org&lt;/a&gt;, of course.  I maintain a page of what I believe to be useful vim links &lt;a href="http://mileszs.github.com/"&gt;on my GitHub Page page&lt;/a&gt;.  If you&amp;#8217;re jonein&amp;#8217; for small tidbits of vim awesomeness once per day, check out &lt;a href="http://twitter.com/vimtips"&gt;my @vimtips account on Twitter&lt;/a&gt;.  Okay.  On to live action&amp;#8230;&lt;/p&gt;
&lt;p&gt;If you&amp;#8217;re ever programming within the Rails framework, then you should be using &lt;a href="http://www.vim.org/scripts/script.php?script_id=1567"&gt;rails.vim&lt;/a&gt;.  It is &lt;em&gt;the&lt;/em&gt; most essential plugin in &lt;a href="http://github.com/mileszs/dotfiles/tree/af0d2a5d1ca3fffd791fa1d55020598fc11697ef/vim/plugin"&gt;my vim plugin arsenal&lt;/a&gt;.  It has a ton of features.  Incredibly helpful shortcuts to opening project files, integration with vim&amp;#8217;s tabs, as well as vim split screens (more on that in a sec), enhanced syntax highlighting, shortcuts for the various script/* scripts&amp;#8230;  I could go on, but I won&amp;#8217;t.  (Type the command &lt;code&gt;:help rails&lt;/code&gt; to browse the extensive help pages.)&lt;/p&gt;
&lt;p&gt;As for splitting, and the way I typically use splits in vim: You may already know that you can type the command &lt;code&gt;:vsplit&lt;/code&gt; to get a vertical split in Vim.  With rails.vim, you can simply type &lt;code&gt;:AV&lt;/code&gt; from a model or controller, and its corresponding test file will open up on the left-hand side of a vertical split.  The &amp;#8216;A&amp;#8217; in the command stands for &amp;#8216;alternate&amp;#8217;, and the &amp;#8216;V&amp;#8217; stands for vertical split, of course.  You can open other files into a vertical split using several of the other rails.vim commands.  For instance, &lt;code&gt;:Rcontroller&lt;/code&gt; would become &lt;code&gt;:RVcontroller&lt;/code&gt;, at which point whatever file you currently have open would move to the right, and the related controller file would open on the left.&lt;/p&gt;
&lt;p&gt;The vim way is the way I want to edit text.  Always.  It&amp;#8217;s incredibly efficient.  Thus, it is the perfect text editor for &lt;strong&gt;The Awesomest IDE Ever&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;&lt;em&gt;This&lt;/em&gt; is &lt;strong&gt;my&lt;/strong&gt; &amp;#8220;IDE&amp;#8221;&lt;/h3&gt;
&lt;p&gt;After some initial configuration, what you see above can be up and running in just a few seconds.  In fact, all I do is type Meta-t (the windows button, or command when I&amp;#8217;m using the &lt;a href="http://www.apple.com/keyboard/"&gt;incomparable Apple keyboard&lt;/a&gt;) to open a terminal, &lt;code&gt;cd devel/project-name&lt;/code&gt;, and then enter the command &lt;code&gt;scrails&lt;/code&gt;.  Really, &lt;code&gt;scrails&lt;/code&gt; is just &lt;a href="http://github.com/mileszs/dotfiles/blob/5a74d9a18c9061b0a8534efb306ca8348ddb7735/bash/aliases.sh"&gt;an alias&lt;/a&gt; for &lt;code&gt;screen -c ~/.rails.screen&lt;/code&gt;.  (It sounds cool, though.  &lt;em&gt;Right?&lt;/em&gt;)  &lt;a href="http://github.com/mileszs/dotfiles/blob/af0d2a5d1ca3fffd791fa1d55020598fc11697ef/rails.screen"&gt;That rails.screen file&lt;/a&gt; looks like this:&lt;/p&gt;
&lt;p&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;
&lt;pre&gt;&lt;br /&gt;
&lt;div class="head"&gt;#&lt;/div&gt;&lt;br /&gt;
&lt;div class="head"&gt;# Initiate a screen session with my&lt;/div&gt;&lt;br /&gt;
&lt;div class="head"&gt;# preferred dev environment&lt;/div&gt;&lt;br /&gt;
&lt;div class="head"&gt;#&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;div class="head"&gt;source /home/mileszs/.screenrc&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;div class="head"&gt;screen -t dev 0&lt;/div&gt;&lt;br /&gt;
&lt;div class="head"&gt;split&lt;/div&gt;&lt;br /&gt;
&lt;div class="head"&gt;resize 40&lt;/div&gt;&lt;br /&gt;
&lt;div class="head"&gt;focus down&lt;/div&gt;&lt;br /&gt;
&lt;div class="head"&gt;screen -t test 1 autotest -f&lt;/div&gt;&lt;br /&gt;
&lt;div class="head"&gt;split -v&lt;/div&gt;&lt;br /&gt;
&lt;div class="head"&gt;focus down&lt;/div&gt;&lt;br /&gt;
&lt;div class="head"&gt;screen -t console 2 script/console&lt;/div&gt;&lt;br /&gt;
&lt;div class="head"&gt;focus top&lt;/div&gt;&lt;br /&gt;
&lt;div class="head"&gt;screen -t server 3 script/server thin&lt;/div&gt;&lt;br /&gt;
&lt;div class="head"&gt;screen -t tail 4 tail -f log/development.log&lt;/div&gt;&lt;br /&gt;
&lt;div class="head"&gt;screen -t misc 5&lt;/div&gt;&lt;br /&gt;
&lt;div class="head"&gt;select 0&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;/pre&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
I&amp;#8217;ll explain:&lt;/p&gt;
&lt;p&gt;First, sort of a &amp;#8216;step 0&amp;#8217;, read in our screenrc file.  Simple enough.&lt;/p&gt;
&lt;p&gt;1. Create a new window, give it the title (See?  &lt;code&gt;-t&lt;/code&gt; for title!) &amp;#8216;dev&amp;#8217;, and let it be window number 0.&lt;br /&gt;
2. Create a horizontal split&lt;br /&gt;
3. Resize the upper window&lt;br /&gt;
4. Focus down to the split we created in step 2&lt;br /&gt;
5. Create a window with a title of &amp;#8216;test&amp;#8217;, and run the command &lt;code&gt;autotest -f&lt;/code&gt;&lt;br /&gt;
6. &amp;#8230;&lt;/p&gt;
&lt;p&gt;You get the idea.  The command &lt;code&gt;split -v&lt;/code&gt; creates our vertical split, which screen thinks of as &amp;#8216;down&amp;#8217; in the list of splits.  At the end, we select window 0 as the window in which we want our cursor when this has finished.&lt;/p&gt;
&lt;h3&gt;Easy.  Right?&lt;/h3&gt;
&lt;p&gt;I don&amp;#8217;t know if this ultimately came across as easy.  It really is.  I encourage you to &lt;a href="http://www.gnu.org/software/screen/"&gt;install GNU screen&lt;/a&gt; and &lt;a href="http://www.vim.org/download.php"&gt;install Vim&lt;/a&gt;.  Give them a shot.  Use all of my config files to start, and then beat those files to hell to see what you end up with.  Vim is great; a great deal of programmers already know that.  With screen, you can take the greatness of vim and create &lt;strong&gt;The Awesomest IDE Ever&lt;/strong&gt;.  Good luck!&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <title>A Git Hook to Push to Integrity</title>
    <link href="/blog/2009/03/03/git-hook-to-integrity.html" />
    <id>tag:mileszs.com,2009-03-03:1236129293</id>
    <updated>2009-03-03T20:14:53-05:00</updated>
    <content type="html">&lt;p&gt;If you&amp;#8217;re using &lt;a href="http://github.com"&gt;GitHub&lt;/a&gt; with &lt;a href="http://integrityapp.com"&gt;Integrity&lt;/a&gt;, &amp;#8220;the easy and fun automated continuous integration server&amp;#8221;, then you probably know how stupid-simple it is to integrate the two.  Seriously.  You just tell GitHub to what URL you wish it to send stuff, and it&amp;#8217;ll do it.  It takes more time for most computers to power up than it takes to get that setup running.  Great, isn&amp;#8217;t it?&lt;/p&gt;
&lt;p&gt;If you&amp;#8217;re hosting your own centralized git repository on your own server, you need to set up your own post-receive hook.  There is a &lt;a href="http://groups.google.com/group/integrityapp/browse_thread/thread/895f3b6198275f6e"&gt;short thread&lt;/a&gt; about doing just that at &lt;a href="http://groups.google.com/group/integrityapp"&gt;Integrity&amp;#8217;s Google Group&lt;/a&gt;.  In fact, that is where our journey begins&amp;#8230;&lt;/p&gt;
&lt;h3&gt;Could someone spell lazy for me?&lt;/h3&gt;
&lt;p&gt;&lt;img src="/images/caturday-lazyday.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;I knew that I needed a post-receive hook that sent the right information to my installation of Integrity, but I really didn&amp;#8217;t want to have to write one myself.  Sure, it might be fun.  It also might be a frustrating waste of time.  So, I went to the Integrity Google Group, searched for &amp;#8220;post receive&amp;#8221;, and came across &lt;a href="http://group"&gt;this thread&lt;/a&gt;    s.google.com/group/integrityapp/browse_thread/thread/895f3b6198275f6e (the same one to which I linked above).  Cool!  That led to &lt;a href="https://gist.github.com/669b761f47e61ec665ad"&gt;this Gist&lt;/a&gt; by whatcould:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Watch out for that wayward &amp;#8216;0&amp;#8217; on line 33 in the Gist.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;
&lt;pre&gt;&lt;span class="dt"&gt;#!/usr/bin/ruby&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span class="c"&gt;# Replace the following by the url given to you in your project&amp;#8217;s edit page&lt;/span&gt;&lt;br /&gt;
&lt;span class="co"&gt;POST_RECEIVE_URL&lt;/span&gt; = &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;#8216;&lt;/span&gt;&lt;span class="k"&gt;http://localhost:4567/my_fancy_project/push&lt;/span&gt;&lt;span class="dl"&gt;&amp;#8217;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span class="c"&gt;#######################################################################&lt;/span&gt;&lt;br /&gt;
&lt;span class="c"&gt;## ##&lt;/span&gt;&lt;br /&gt;
&lt;span class="c"&gt;## == DON&amp;#8217;T EDIT ANYTHING BELOW UNLESS YOU KNOW WHAT YOU&amp;#8217;RE DOING == ##&lt;/span&gt;&lt;br /&gt;
&lt;span class="c"&gt;## ##&lt;/span&gt;&lt;br /&gt;
&lt;span class="c"&gt;#######################################################################&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
require &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;#8216;&lt;/span&gt;&lt;span class="k"&gt;net/http&lt;/span&gt;&lt;span class="dl"&gt;&amp;#8217;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
require &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;#8216;&lt;/span&gt;&lt;span class="k"&gt;uri&lt;/span&gt;&lt;span class="dl"&gt;&amp;#8217;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
old_head, new_head, ref = &lt;span class="co"&gt;STDIN&lt;/span&gt;.gets.split&lt;br /&gt;
&lt;br /&gt;
&lt;span class="c"&gt;# on solaris, ruby is choking on various shell commands, the quotes in format.&lt;/span&gt;&lt;br /&gt;
&lt;span class="c"&gt;# maybe it&amp;#8217;s running sh? (though it seems to report bash)?&lt;/span&gt;&lt;br /&gt;
&lt;span class="c"&gt;# the `bash -c &amp;#8230;` gets around it, anyhow:&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
revisions = &lt;span class="sh"&gt;&lt;span class="dl"&gt;%x{&lt;/span&gt;&lt;span class="k"&gt;bash -c &amp;quot;git-rev-list &amp;#8212;pretty=format:&amp;#8216;%H %ai&amp;#8217; &lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;new_head&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt; ^&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;old_head&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt;&amp;quot;&lt;/span&gt;&lt;span class="dl"&gt;}&lt;/span&gt;&lt;/span&gt;.split(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="ch"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)&lt;br /&gt;
&lt;br /&gt;
rev_list = revisions.inject([]) &lt;span class="r"&gt;do&lt;/span&gt; |list,line|&lt;br /&gt;
  &lt;span class="r"&gt;if&lt;/span&gt; line[&lt;span class="i"&gt;0&lt;/span&gt;..&lt;span class="i"&gt;5&lt;/span&gt;] == &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;#8216;&lt;/span&gt;&lt;span class="k"&gt;commit&lt;/span&gt;&lt;span class="dl"&gt;&amp;#8217;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
    list&lt;br /&gt;
  &lt;span class="r"&gt;else&lt;/span&gt;&lt;br /&gt;
    list &amp;lt;&amp;lt; &lt;span class="s"&gt;&lt;span class="dl"&gt;%Q(&lt;/span&gt;&lt;span class="k"&gt;{&amp;quot;id&amp;quot;:&amp;quot;&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;line[&lt;span class="i"&gt;0&lt;/span&gt;..&lt;span class="i"&gt;40&lt;/span&gt;]&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt;&amp;quot;,&amp;quot;timestamp&amp;quot;:&amp;quot;&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;line[&lt;span class="i"&gt;41&lt;/span&gt;..&lt;span class="i"&gt;66&lt;/span&gt;]&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt;&amp;quot;}&lt;/span&gt;&lt;span class="dl"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
  &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;
&lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span class="co"&gt;Net&lt;/span&gt;::&lt;span class="co"&gt;HTTP&lt;/span&gt;.post_form(&lt;span class="co"&gt;URI&lt;/span&gt;.parse(&lt;span class="co"&gt;POST_RECEIVE_URL&lt;/span&gt;), {&lt;br /&gt;
 &lt;span class="sy"&gt;:payload&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;%Q(&lt;/span&gt;&lt;span class="k"&gt;{&amp;quot;ref&amp;quot;:&amp;quot;&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;ref&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt;&amp;quot;, &amp;quot;commits&amp;quot;:[&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;rev_list.join(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;#8216;&lt;/span&gt;&lt;span class="k"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;&amp;#8217;&lt;/span&gt;&lt;/span&gt;)&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt;]}&lt;/span&gt;&lt;span class="dl"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
})&lt;br /&gt;
&lt;/pre&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
Hurry Up!&lt;/h3&gt;
&lt;p&gt;You can just copy and paste that script to &lt;code&gt;GIT_REPO_ROOT/hooks/post-receive&lt;/code&gt;, &lt;code&gt;chmod +x GIT_REPO_ROOT/hooks/post-receive&lt;/code&gt;, and you should be good to go.  The next time that you &lt;code&gt;git push&lt;/code&gt;, it&amp;#8217;ll push the necessary information to Integrity.  Eventually.  It takes a while.  The waiting was driving me nuts.  One of the great things about git is how freakin&amp;#8217; fast &lt;em&gt;everything&lt;/em&gt; is.  This script was killing me.  Perhaps I&amp;#8217;m just really impatient.  Maybe I&amp;#8217;m doing something wrong.  I have a solution, though.&lt;/p&gt;
&lt;h3&gt;And Then There Was Awesome&lt;/h3&gt;
&lt;p&gt;We&amp;#8217;re going to use the &amp;#8220;Daemons&amp;#8221; gem to make this freakin&amp;#8217; fast.  OK, technically, it doesn&amp;#8217;t speed anything up.  It just gets out of the way.  Whatever.  Do this:&lt;/p&gt;
&lt;p&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;
&lt;pre&gt;  sudo gem install daemons&lt;br /&gt;
&lt;/pre&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
Now we just have to add a couple lines to the post-receive hook:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This version is available as a gist &lt;a href="https://gist.github.com/4d81e30bbc4aac556dec"&gt;here&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;
&lt;pre&gt;&lt;span class="dt"&gt;#!/usr/bin/ruby&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span class="c"&gt;# Replace the following by the url given to you in your project&amp;#8217;s edit page&lt;/span&gt;&lt;br /&gt;
&lt;span class="co"&gt;POST_RECEIVE_URL&lt;/span&gt; = &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;#8216;&lt;/span&gt;&lt;span class="k"&gt;http://localhost:4567/my_fancy_project/push&lt;/span&gt;&lt;span class="dl"&gt;&amp;#8217;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span class="c"&gt;#######################################################################&lt;/span&gt;&lt;br /&gt;
&lt;span class="c"&gt;## ##&lt;/span&gt;&lt;br /&gt;
&lt;span class="c"&gt;## == DON&amp;#8217;T EDIT ANYTHING BELOW UNLESS YOU KNOW WHAT YOU&amp;#8217;RE DOING == ##&lt;/span&gt;&lt;br /&gt;
&lt;span class="c"&gt;## ##&lt;/span&gt;&lt;br /&gt;
&lt;span class="c"&gt;#######################################################################&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
require &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;#8216;&lt;/span&gt;&lt;span class="k"&gt;net/http&lt;/span&gt;&lt;span class="dl"&gt;&amp;#8217;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
require &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;#8216;&lt;/span&gt;&lt;span class="k"&gt;uri&lt;/span&gt;&lt;span class="dl"&gt;&amp;#8217;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
require &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;#8216;&lt;/span&gt;&lt;span class="k"&gt;rubygems&lt;/span&gt;&lt;span class="dl"&gt;&amp;#8217;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
require &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;#8216;&lt;/span&gt;&lt;span class="k"&gt;daemons&lt;/span&gt;&lt;span class="dl"&gt;&amp;#8217;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
old_head, new_head, ref = &lt;span class="co"&gt;STDIN&lt;/span&gt;.gets.split&lt;br /&gt;
&lt;br /&gt;
&lt;span class="c"&gt;# on solaris, ruby is choking on various shell commands, the quotes in format.&lt;/span&gt;&lt;br /&gt;
&lt;span class="c"&gt;# maybe it&amp;#8217;s running sh? (though it seems to report bash)?&lt;/span&gt;&lt;br /&gt;
&lt;span class="c"&gt;# the `bash -c &amp;#8230;` gets around it, anyhow:&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
revisions = &lt;span class="sh"&gt;&lt;span class="dl"&gt;%x{&lt;/span&gt;&lt;span class="k"&gt;bash -c &amp;quot;git-rev-list &amp;#8212;pretty=format:&amp;#8216;%H %ai&amp;#8217; &lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;new_head&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt; ^&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;old_head&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt;&amp;quot;&lt;/span&gt;&lt;span class="dl"&gt;}&lt;/span&gt;&lt;/span&gt;.split(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="ch"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)&lt;br /&gt;
&lt;br /&gt;
rev_list = revisions.inject([]) &lt;span class="r"&gt;do&lt;/span&gt; |list,line|&lt;br /&gt;
  &lt;span class="r"&gt;if&lt;/span&gt; line[&lt;span class="i"&gt;0&lt;/span&gt;..&lt;span class="i"&gt;5&lt;/span&gt;] == &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;#8216;&lt;/span&gt;&lt;span class="k"&gt;commit&lt;/span&gt;&lt;span class="dl"&gt;&amp;#8217;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
    list&lt;br /&gt;
  &lt;span class="r"&gt;else&lt;/span&gt;&lt;br /&gt;
    list &amp;lt;&amp;lt; &lt;span class="s"&gt;&lt;span class="dl"&gt;%Q(&lt;/span&gt;&lt;span class="k"&gt;{&amp;quot;id&amp;quot;:&amp;quot;&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;line[&lt;span class="i"&gt;0&lt;/span&gt;..&lt;span class="i"&gt;40&lt;/span&gt;]&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt;&amp;quot;,&amp;quot;timestamp&amp;quot;:&amp;quot;&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;line[&lt;span class="i"&gt;41&lt;/span&gt;..&lt;span class="i"&gt;66&lt;/span&gt;]&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt;&amp;quot;}&lt;/span&gt;&lt;span class="dl"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
  &lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;
&lt;span class="r"&gt;end&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span class="co"&gt;Daemons&lt;/span&gt;.daemonize&lt;br /&gt;
&lt;br /&gt;
&lt;span class="co"&gt;Net&lt;/span&gt;::&lt;span class="co"&gt;HTTP&lt;/span&gt;.post_form(&lt;span class="co"&gt;URI&lt;/span&gt;.parse(&lt;span class="co"&gt;POST_RECEIVE_URL&lt;/span&gt;), {&lt;br /&gt;
 &lt;span class="sy"&gt;:payload&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;%Q(&lt;/span&gt;&lt;span class="k"&gt;{&amp;quot;ref&amp;quot;:&amp;quot;&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;ref&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt;&amp;quot;, &amp;quot;commits&amp;quot;:[&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="idl"&gt;#{&lt;/span&gt;rev_list.join(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;#8216;&lt;/span&gt;&lt;span class="k"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;&amp;#8217;&lt;/span&gt;&lt;/span&gt;)&lt;span class="idl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt;]}&lt;/span&gt;&lt;span class="dl"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
})&lt;br /&gt;
&lt;/pre&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
&lt;code&gt;Daemons.daemonize&lt;/code&gt; will shove our currently-running script to the background and let git get on with gittin&amp;#8217;.  It&amp;#8217;s simple.  It&amp;#8217;s fast.  Now, it&amp;#8217;s &lt;strong&gt;legendary&lt;/strong&gt;!&lt;br /&gt;
&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/legendary-barney.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Image borrowed from &lt;a href="http://southerndesigner.deviantart.com/art/Legendary-Barney-Stinson-102733576"&gt;SoutherDesigner at DeviantArt&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <title>My New Home</title>
    <link href="/blog/2009/02/07/my-new-home.html" />
    <id>tag:mileszs.com,2009-02-07:1234068869</id>
    <updated>2009-02-07T23:54:29-05:00</updated>
    <content type="html">&lt;p&gt;I &lt;em&gt;finally&lt;/em&gt; did it.  I switched to a static blog, powered by &lt;a href="http://webby.rubyforge.org/"&gt;Webby&lt;/a&gt;.  I have been &lt;a href="/2008/07/10/too-many-choices.html"&gt;talking about doing this&lt;/a&gt; since July of 2008.  Hey, I came in under a year!  Sweet!&lt;/p&gt;
&lt;h4&gt;Git and Webby, Sitting in a Tree&lt;/h4&gt;
&lt;p&gt;&lt;img src="/images/kissing-squirrels-tree.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Essentially, Webby and &lt;a href="http://git-scm.com"&gt;Git&lt;/a&gt; power this blahg.  I tell Webby I want to post a new entry.  When finished, I commit it and push it to the &amp;#8220;central&amp;#8221; repository on my server.  A post-recieve hook pulls the update into the web-accessible directory.  BAM!  It&amp;#8217;s like magic.&lt;/p&gt;
&lt;p&gt;So, basically, this:&lt;/p&gt;
&lt;p&gt;&lt;div class="CodeRay"&gt;&lt;br /&gt;
&lt;pre&gt;webby blog:post my-new-home&lt;br /&gt;
# my-new-home.txt is opened in my $EDITOR (VIM!),&lt;br /&gt;
# with the proper meta-data already filled in&lt;br /&gt;
# according to Webby rules.&lt;br /&gt;
#&lt;br /&gt;
# After I finish writing:&lt;br /&gt;
webby build&lt;br /&gt;
git commit -a -m &amp;quot;POST: My New Home&amp;quot;&lt;br /&gt;
git push origin master&lt;br /&gt;
&lt;/pre&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
It&amp;#8217;s that simple.&lt;/p&gt;
&lt;p&gt;Well, it could be, anyway.  In reality, I&amp;#8217;m still tweaking design aspects, and I am still tweaking some content.  From time to time, in the midst of working on a post, I will find that some other aspect of the site needs tweaked.  I like to see what my post looks like after nearly every paragraph because I have some sort of mild OCD or something.  Thankfully, I can simply run &lt;code&gt;webby autobuild&lt;/code&gt; in the background.  This launches an instance of &lt;a href="http://www.webrick.org/"&gt;WEBrick&lt;/a&gt;, opens your browser, and points it at &lt;code&gt;localhost:4331&lt;/code&gt;.  Whenever a page is saved, it updates the relevant output, and serves it up.  This has the added benefit of not needing to run &lt;code&gt;webby build&lt;/code&gt; before committing.  It&amp;#8217;s fan-freakin&amp;#8217;-tastic.&lt;/p&gt;
&lt;h3&gt;JS-Kit Comments&lt;/h3&gt;
&lt;h4&gt;The Good&lt;/h4&gt;
&lt;p&gt;I went with &lt;a href="http://js-kit.com/comments"&gt;JS-Kit Comments&lt;/a&gt; over Disqus (or Intense Debate, etc.).  Look at the &lt;a href="http://js-kit.com/comments/features.html"&gt;feature list&lt;/a&gt; yourself.  There are a &lt;strong&gt;ton&lt;/strong&gt; of features: OpenID integration (and Facebook Connect), Akismet SPAM protection, full custimization&amp;#8230;.  The list is lengthy.  Check it out.&lt;/p&gt;
&lt;p&gt;The first thing that struck me about JS-Kit was their site. While perhaps not as pretty as the Disqus site, the site proved a lot easier to find the exact information for which I was looking.&lt;/p&gt;
&lt;p&gt;It also helps that I have gotten helpful replies on Twitter from &lt;a href="http://twitter.com/JSKitSupport"&gt;JSKitSupport&lt;/a&gt;.  There are several people from JS-Kit on Twitter, and they are pretty active.  I know, Disqus does this, too.  They never contacted me, though.&lt;/p&gt;
&lt;p&gt;JS-Kit has several other widgets to go with their Comments system.&lt;/p&gt;
&lt;h4&gt;The Not-So-Good&lt;/h4&gt;
&lt;p&gt;Honestly, the name &amp;#8216;JS-Kit Comments&amp;#8217; sucks.  It sounds like some half-assed javascript library from 2001.  Their logo isn&amp;#8217;t great.  Disqus is a &lt;a href="http://ycombinator.com"&gt;YCombinator&lt;/a&gt; company, and people freakin&amp;#8217; love them some YC.&lt;/p&gt;
&lt;p&gt;Give me a couple months, and I will probably be able to produce more bad aspects of JS-Kit with validity.&lt;/p&gt;
&lt;h4&gt;The Other&lt;/h4&gt;
&lt;p&gt;I have not gotten my comments from soyunperdedor.com transferred yet.  Sorry.&lt;/p&gt;
&lt;h3&gt;New Domain(s)&lt;/h3&gt;
&lt;p&gt;I&amp;#8217;ve decided to use a more obvious domain &amp;#8216;mileszs.com&amp;#8217;.  (I do also own mileszs.org, and mileszs.net, which both hit soyunperdedor.com at the time of this writing.  They won&amp;#8217;t always.)  While the domain soyunperdedor.com still makes me giggle a bit, it wasn&amp;#8217;t very practical.  It was difficult for some to remember, cumbersome for some to spell, and made no sense unless you either 1) know Spanish, or 2) know &lt;a href="http://www.last.fm/music/Beck/_/Loser"&gt;Beck&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/beck-mellow-gold.jpg" alt="" /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;#8220;You know, like in that Beck song, &amp;#8216;&lt;strong&gt;Loser&lt;/strong&gt;&amp;#8217;.&amp;#8221; &lt;br /&gt;&amp;#8220;&lt;em&gt;Excuse&lt;/em&gt; me?!  Screw you, too, &lt;em&gt;buddy&lt;/em&gt;.&amp;#8221;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Soon enough, soyunperdedor.com will redirect to mileszs.com.&lt;/p&gt;
&lt;h3&gt;The Design&lt;/h3&gt;
&lt;p&gt;Not much to talk about with regards to the design.  The most interesting aspect probably is that I am using the &lt;a href="http://960.gs/"&gt;960 Grid System&lt;/a&gt;.  I will admit that I never dove in and used &lt;a href="http://www.blueprintcss.org/"&gt;Blueprint CSS&lt;/a&gt;.  I did use &lt;a href="http://developer.yahoo.com/yui/grids/"&gt;YUI Grids CSS&lt;/a&gt; a bit.  I found 960 to be easier with which to work compared to YUI, and I liked the grid and naming philosophies better than Blueprint.  I think it&amp;#8217;s a matter of opinion.&lt;/p&gt;
&lt;h3&gt;The End&lt;/h3&gt;
&lt;p&gt;That&amp;#8217;s it.  I get to &lt;a href="http://tom.preston-werner.com/2008/11/17/blogging-like-a-hacker.html"&gt;blog like a hacker&lt;/a&gt;, although not using Mr. Preston-Werner&amp;#8217;s &lt;a href="http://github.com/mojombo/jekyll/tree/master"&gt;Jekyll&lt;/a&gt;.  (Webby is a bit more powerful, and supports &lt;a href="http://haml.hamptoncatlin.com/docs/rdoc/classes/Sass.html"&gt;Sass&lt;/a&gt; and &lt;a href="http://haml.hamptoncatlin.com/"&gt;Haml&lt;/a&gt;, which is &lt;strong&gt;huge&lt;/strong&gt;.  Jekyll is good, though.  I use it for &lt;a href="http://mileszs.github.com/"&gt;Vim Links of Worth and Valor&lt;/a&gt;, my GitHub Pages page.)  I really love doing everything from the command line, instead of copying and pasting into a web form, or something equally as lame.  Of course, there&amp;#8217;s the added benefit of it not doing a &lt;em&gt;single&lt;/em&gt; database request.  So, it&amp;#8217;s fast.  It scales.  It brings all the boys to the yard.  &lt;em&gt;Damn right&lt;/em&gt;, it&amp;#8217;s better than yours.&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <title>Shoulda Macros for Common Plugins</title>
    <link href="/blog/2008/09/21/shoulda-macros-for-common-plugins.html" />
    <id>tag:mileszs.com,2008-09-21:1221970355</id>
    <updated>2008-09-21T04:12:35Z</updated>
    <content type="html">&lt;p&gt;If you haven't looked into &lt;a href="http://thoughtbot.com/projects/shoulda"&gt;Shoulda&lt;/a&gt;, maybe you should.  It's a plugin (and now gem) that works "on top of" Test::Unit.  It truly does make it "easy to write elegant, understandable, and maintainable tests". &lt;/p&gt;
&lt;p&gt;The following are a few Shoulda macros that I use for the plugins &lt;strong&gt;acts_as_ferret&lt;/strong&gt;, &lt;strong&gt;acts_as_taggable_on_steroids&lt;/strong&gt;, and &lt;strong&gt;acts_as_list&lt;/strong&gt;.  I recommend creating a file 'test/shoulda_macros/plugins.rb', and placing these macros in there.  For example:&lt;/p&gt;
&lt;pre&gt;
&lt;div class="codeblock"&gt;&lt;div class="ruby" style="font-family: monospace;"&gt;&lt;span style="color: #9966CC; font-weight: bold;"&gt;class&lt;/span&gt; &lt;span style="color: #CC0066; font-weight: bold;"&gt;Test&lt;/span&gt;::Unit::TestCase&lt;br/&gt;&lt;br/&gt;&lt;span style="color: #008000; font-style: italic;"&gt;# Shoulda macros here&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;&lt;span style="color: #9966CC; font-weight: bold;"&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/pre&gt;&lt;h3&gt;Acts As Ferret&lt;/h3&gt;
&lt;pre&gt;
&lt;div class='codeblock'&gt;&lt;div class='ruby' style='font-family: monospace;'&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;def&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;should_act_as_ferret&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;*fields&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;&lt;br/&gt;    klass = &lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;name&lt;/span&gt;.&lt;span style='color: #CC0066; font-weight: bold;'&gt;gsub&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;/&lt;span style='color: #CC0066; font-weight: bold;'&gt;Test&lt;/span&gt;$/, ''&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;constantize&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;    should &lt;span style='color: #996600;'&gt;"include ActsAsFerret methods"&lt;/span&gt; &lt;span style='color: #9966CC; font-weight: bold;'&gt;do&lt;/span&gt;&lt;br/&gt;      assert klass.&lt;span style='color: #9900CC;'&gt;extended_by&lt;/span&gt;.&lt;span style='color: #9966CC; font-weight: bold;'&gt;include&lt;/span&gt;?&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;ActsAsFerret::ClassMethods&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;&lt;br/&gt;      assert klass.&lt;span style='color: #9966CC; font-weight: bold;'&gt;include&lt;/span&gt;?&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;ActsAsFerret::InstanceMethods&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;&lt;br/&gt;      assert klass.&lt;span style='color: #9966CC; font-weight: bold;'&gt;include&lt;/span&gt;?&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;ActsAsFerret::MoreLikeThis::InstanceMethods&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;&lt;br/&gt;      assert klass.&lt;span style='color: #9966CC; font-weight: bold;'&gt;include&lt;/span&gt;?&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;ActsAsFerret::ResultAttributes&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;    fields.&lt;span style='color: #9900CC;'&gt;each&lt;/span&gt; &lt;span style='color: #9966CC; font-weight: bold;'&gt;do&lt;/span&gt; |f|&lt;br/&gt;      should &lt;span style='color: #996600;'&gt;"create an index for field named #{f}"&lt;/span&gt; &lt;span style='color: #9966CC; font-weight: bold;'&gt;do&lt;/span&gt;&lt;br/&gt;        assert klass.&lt;span style='color: #9900CC;'&gt;aaf_configuration&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;[&lt;/span&gt;:fields&lt;span style='color: #006600; font-weight: bold;'&gt;]&lt;/span&gt;.&lt;span style='color: #9966CC; font-weight: bold;'&gt;include&lt;/span&gt;?&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;f&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;&lt;br/&gt;      &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/pre&gt;&lt;p&gt;Use it like:&lt;/p&gt;
&lt;pre&gt;
&lt;div class='codeblock'&gt;&lt;div class='ruby' style='font-family: monospace;'&gt;should_act_as_ferret :any, :fields, :i_may, :have, :specified&lt;/div&gt;&lt;/div&gt;
&lt;/pre&gt;&lt;h3&gt;Acts As Taggable On Steroids&lt;/h3&gt;
&lt;pre&gt;
&lt;div class='codeblock'&gt;&lt;div class='ruby' style='font-family: monospace;'&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;def&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;should_act_as_taggable_on_steroids&lt;/span&gt;&lt;br/&gt;    klass = &lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;name&lt;/span&gt;.&lt;span style='color: #CC0066; font-weight: bold;'&gt;gsub&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;/&lt;span style='color: #CC0066; font-weight: bold;'&gt;Test&lt;/span&gt;$/, ''&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;constantize&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;    should &lt;span style='color: #996600;'&gt;"include ActsAsTaggableOnSteroids methods"&lt;/span&gt; &lt;span style='color: #9966CC; font-weight: bold;'&gt;do&lt;/span&gt;&lt;br/&gt;      assert klass.&lt;span style='color: #9900CC;'&gt;extended_by&lt;/span&gt;.&lt;span style='color: #9966CC; font-weight: bold;'&gt;include&lt;/span&gt;?&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;ActiveRecord::Acts::Taggable::ClassMethods&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;&lt;br/&gt;      assert klass.&lt;span style='color: #9900CC;'&gt;extended_by&lt;/span&gt;.&lt;span style='color: #9966CC; font-weight: bold;'&gt;include&lt;/span&gt;?&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;ActiveRecord::Acts::Taggable::SingletonMethods&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;&lt;br/&gt;      assert klass.&lt;span style='color: #9966CC; font-weight: bold;'&gt;include&lt;/span&gt;?&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;ActiveRecord::Acts::Taggable::InstanceMethods&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;    should_have_many :taggings, :tags&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/pre&gt;&lt;h3&gt;Acts As List&lt;/h3&gt;
&lt;pre&gt;
&lt;div class='codeblock'&gt;&lt;div class='ruby' style='font-family: monospace;'&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;def&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;should_act_as_list&lt;/span&gt;&lt;br/&gt;    klass = &lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;name&lt;/span&gt;.&lt;span style='color: #CC0066; font-weight: bold;'&gt;gsub&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;/&lt;span style='color: #CC0066; font-weight: bold;'&gt;Test&lt;/span&gt;$/, ''&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;constantize&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;    context &lt;span style='color: #996600;'&gt;"To support acts_as_list"&lt;/span&gt; &lt;span style='color: #9966CC; font-weight: bold;'&gt;do&lt;/span&gt;&lt;br/&gt;      should_have_db_column&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;'position', :type =&amp;gt; :&lt;span style='color: #CC0066; font-weight: bold;'&gt;integer&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;    should &lt;span style='color: #996600;'&gt;"include ActsAsList methods"&lt;/span&gt; &lt;span style='color: #9966CC; font-weight: bold;'&gt;do&lt;/span&gt;&lt;br/&gt;      assert klass.&lt;span style='color: #9966CC; font-weight: bold;'&gt;include&lt;/span&gt;?&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;ActiveRecord::Acts::List::InstanceMethods&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;    should_have_instance_methods :acts_as_list_class, :position_column, :scope_condition&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;  These tests are quite basic.  Except for checking the fields on &lt;strong&gt;acts_as_ferret&lt;/strong&gt;, there isn't any testing of each plugin's options.&lt;/p&gt;
&lt;p&gt;These macros are available at &lt;a href='http://github.com/mileszs/shoulda_macros'&gt;http://github.com/mileszs/shoulda_macros&lt;/a&gt;.  You can find more macros at the Shoulda wiki on GitHub, &lt;a href='http://github.com/thoughtbot/shoulda/wikis/example-macros'&gt;http://github.com/thoughtbot/shoulda/wikis/example-macros&lt;/a&gt;.  If you write some of your own shoulda macros, you should link to them on the wiki as well.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>From class_eval to Modules</title>
    <link href="/blog/2008/09/14/from-class-eval-to-modules.html" />
    <id>tag:mileszs.com,2008-09-14:1221403934</id>
    <updated>2008-09-14T14:52:14Z</updated>
    <content type="html">&lt;p&gt;Once in a while, it's nice to actually go back and look at old code.  Not because that old code makes you feel good about yourself at all -- quite the opposite -- but because you feel good about yourself after you clean it up a little bit.  &lt;/p&gt;
&lt;p&gt;Ola Bini's &lt;a href='http://olabini.com/blog/2008/09/evil-hook-methods/'&gt;Evil Hook Methods?&lt;/a&gt; got me thinking about how I extend classes in a dusty Rails application.  It reminded me of Jay Fields' &lt;a href='http://blog.jayfields.com/2008/07/ruby-underuse-of-modules.html'&gt;Ruby: Underuse of Modules&lt;/a&gt;.  Of course, Jay also &lt;a href='http://blog.jayfields.com/2008/09/domain-specific-languages-dont-follow.html'&gt;weighed in on&lt;/a&gt; Ola's concerns.  All of these articles have led me to take a look at some simple extensions I had made to Time for this particular Dusty Rails App (DRA, from now on).  First, this is what I had before:&lt;/p&gt;
&lt;pre&gt;
&lt;div class='codeblock'&gt;&lt;div class='ruby' style='font-family: monospace;'&gt;Time.&lt;span style='color: #9900CC;'&gt;class_eval&lt;/span&gt; &lt;span style='color: #9966CC; font-weight: bold;'&gt;do&lt;/span&gt; &lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;def&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;next_sunday&lt;/span&gt;&lt;br/&gt;    t = Time.&lt;span style='color: #9900CC;'&gt;local&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;Time.&lt;span style='color: #9900CC;'&gt;now&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;year&lt;/span&gt;, Time.&lt;span style='color: #9900CC;'&gt;now&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;month&lt;/span&gt;, Time.&lt;span style='color: #9900CC;'&gt;now&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;day&lt;/span&gt;, &lt;span style='color: #006666;'&gt;0&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt; &lt;span style='color: #008000; font-style: italic;'&gt;# Today at midnight&lt;/span&gt;&lt;br/&gt;    t += &lt;span style='color: #006666;'&gt;60&lt;/span&gt;*&lt;span style='color: #006666;'&gt;60&lt;/span&gt;*&lt;span style='color: #006666;'&gt;24&lt;/span&gt; &lt;span style='color: #9966CC; font-weight: bold;'&gt;until&lt;/span&gt; t.&lt;span style='color: #9900CC;'&gt;wday&lt;/span&gt; == &lt;span style='color: #006666;'&gt;0&lt;/span&gt;&lt;br/&gt;    t &lt;span style='color: #008000; font-style: italic;'&gt;# Should now be next Sunday at midnight&lt;/span&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;def&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;nth_wday&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;n, wday, month, year&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;if&lt;/span&gt; &lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;!n.&lt;span style='color: #9900CC;'&gt;between&lt;/span&gt;? &lt;span style='color: #006666;'&gt;1&lt;/span&gt;,&lt;span style='color: #006666;'&gt;5&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt; ||&lt;br/&gt;       &lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;!wday.&lt;span style='color: #9900CC;'&gt;between&lt;/span&gt;? &lt;span style='color: #006666;'&gt;0&lt;/span&gt;,&lt;span style='color: #006666;'&gt;6&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt; ||&lt;br/&gt;       &lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;!month.&lt;span style='color: #9900CC;'&gt;between&lt;/span&gt;? &lt;span style='color: #006666;'&gt;1&lt;/span&gt;,&lt;span style='color: #006666;'&gt;12&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;&lt;br/&gt;      &lt;span style='color: #CC0066; font-weight: bold;'&gt;raise&lt;/span&gt; ArgumentError&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;    t = Time.&lt;span style='color: #9900CC;'&gt;local&lt;/span&gt; year, month, &lt;span style='color: #006666;'&gt;1&lt;/span&gt;&lt;br/&gt;    first = t.&lt;span style='color: #9900CC;'&gt;wday&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;if&lt;/span&gt; first == wday&lt;br/&gt;      fwd = &lt;span style='color: #006666;'&gt;1&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;elsif&lt;/span&gt; first &amp;lt; wday&lt;br/&gt;      fwd = wday - first + &lt;span style='color: #006666;'&gt;1&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;elsif&lt;/span&gt; first &amp;gt; wday&lt;br/&gt;      fwd = &lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;wday&lt;span style='color: #006666;'&gt;+7&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt; - first + &lt;span style='color: #006666;'&gt;1&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;    target = fwd + &lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;n&lt;span style='color: #006666;'&gt;-1&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;*&lt;span style='color: #006666;'&gt;7&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;begin&lt;/span&gt;&lt;br/&gt;      t2 = Time.&lt;span style='color: #9900CC;'&gt;local&lt;/span&gt; year, month, target&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;rescue&lt;/span&gt; ArgumentError&lt;br/&gt;      &lt;span style='color: #0000FF; font-weight: bold;'&gt;return&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;nil&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;if&lt;/span&gt; t2.&lt;span style='color: #9900CC;'&gt;mday&lt;/span&gt; == target&lt;br/&gt;      t2&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;else&lt;/span&gt;&lt;br/&gt;      &lt;span style='color: #0000FF; font-weight: bold;'&gt;nil&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;def&lt;/span&gt; after_hours?&lt;br/&gt;    result = &lt;span style='color: #0000FF; font-weight: bold;'&gt;true&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #008000; font-style: italic;'&gt;# M-F, 8am to 6pm are business hours&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;if&lt;/span&gt; &lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;&lt;span style='color: #006666;'&gt;1&lt;/span&gt;..&lt;span style='color: #006666;'&gt;5&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;.&lt;span style='color: #9966CC; font-weight: bold;'&gt;include&lt;/span&gt;?&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;&lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;wday&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt; &amp;amp;&amp;amp; &lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;&lt;span style='color: #006666;'&gt;8&lt;/span&gt;..&lt;span style='color: #006666;'&gt;18&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;.&lt;span style='color: #9966CC; font-weight: bold;'&gt;include&lt;/span&gt;?&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;&lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;hour&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt; &lt;br/&gt;      result = &lt;span style='color: #0000FF; font-weight: bold;'&gt;false&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #008000; font-style: italic;'&gt;# unless it's a holiday!&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;if&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;holiday&lt;/span&gt;?&lt;br/&gt;      result = &lt;span style='color: #0000FF; font-weight: bold;'&gt;true&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;    result&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;def&lt;/span&gt; holiday?&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;unless&lt;/span&gt; new_years_day? || &lt;br/&gt;       mlk_jr_day? ||&lt;br/&gt;       memorial_day? ||&lt;br/&gt;       independence_day? ||&lt;br/&gt;       labor_day? ||&lt;br/&gt;       thanksgiving? ||&lt;br/&gt;       christmas_day?&lt;br/&gt;      &lt;span style='color: #0000FF; font-weight: bold;'&gt;return&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;false&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #0000FF; font-weight: bold;'&gt;true&lt;/span&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;def&lt;/span&gt; new_years_day?&lt;br/&gt;    &lt;span style='color: #0000FF; font-weight: bold;'&gt;return&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;false&lt;/span&gt; &lt;span style='color: #9966CC; font-weight: bold;'&gt;unless&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;yday&lt;/span&gt; == &lt;span style='color: #006666;'&gt;1&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #0000FF; font-weight: bold;'&gt;true&lt;/span&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;def&lt;/span&gt; mlk_jr_day?&lt;br/&gt;    &lt;span style='color: #0000FF; font-weight: bold;'&gt;return&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;false&lt;/span&gt; &lt;span style='color: #9966CC; font-weight: bold;'&gt;unless&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;yday&lt;/span&gt; == Time.&lt;span style='color: #9900CC;'&gt;nth_wday&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;&lt;span style='color: #006666;'&gt;3&lt;/span&gt;,&lt;span style='color: #006666;'&gt;1&lt;/span&gt;,&lt;span style='color: #006666;'&gt;1&lt;/span&gt;,&lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;year&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #0000FF; font-weight: bold;'&gt;true&lt;/span&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;def&lt;/span&gt; memorial_day?&lt;br/&gt;    last_day_of_month = Time.&lt;span style='color: #9900CC;'&gt;local&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;&lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;year&lt;/span&gt;, &lt;span style='color: #006666;'&gt;5&lt;/span&gt;, &lt;span style='color: #006666;'&gt;31&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;&lt;br/&gt;    d = last_day_of_month.&lt;span style='color: #9900CC;'&gt;mday&lt;/span&gt; - last_day_of_month.&lt;span style='color: #9900CC;'&gt;wday&lt;/span&gt; + &lt;span style='color: #006666;'&gt;1&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #0000FF; font-weight: bold;'&gt;return&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;false&lt;/span&gt; &lt;span style='color: #9966CC; font-weight: bold;'&gt;unless&lt;/span&gt; Time.&lt;span style='color: #9900CC;'&gt;now&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;yday&lt;/span&gt; == Time.&lt;span style='color: #9900CC;'&gt;local&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;&lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;year&lt;/span&gt;, &lt;span style='color: #006666;'&gt;5&lt;/span&gt;, d&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #0000FF; font-weight: bold;'&gt;true&lt;/span&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;def&lt;/span&gt; indepence_day?&lt;br/&gt;    &lt;span style='color: #0000FF; font-weight: bold;'&gt;return&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;false&lt;/span&gt; &lt;span style='color: #9966CC; font-weight: bold;'&gt;unless&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;month&lt;/span&gt; == &lt;span style='color: #006666;'&gt;7&lt;/span&gt; &amp;amp;&amp;amp; &lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;mday&lt;/span&gt; == &lt;span style='color: #006666;'&gt;4&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #0000FF; font-weight: bold;'&gt;true&lt;/span&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;def&lt;/span&gt; labor_day?&lt;br/&gt;    &lt;span style='color: #0000FF; font-weight: bold;'&gt;return&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;false&lt;/span&gt; &lt;span style='color: #9966CC; font-weight: bold;'&gt;unless&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;yday&lt;/span&gt; == Time.&lt;span style='color: #9900CC;'&gt;nth_wday&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;&lt;span style='color: #006666;'&gt;1&lt;/span&gt;,&lt;span style='color: #006666;'&gt;1&lt;/span&gt;,&lt;span style='color: #006666;'&gt;9&lt;/span&gt;,&lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;year&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;yday&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #0000FF; font-weight: bold;'&gt;true&lt;/span&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;def&lt;/span&gt; thanksgiving?&lt;br/&gt;    &lt;span style='color: #0000FF; font-weight: bold;'&gt;return&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;false&lt;/span&gt; &lt;span style='color: #9966CC; font-weight: bold;'&gt;unless&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;yday&lt;/span&gt; == &lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9966CC; font-weight: bold;'&gt;class&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;nth_wday&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;&lt;span style='color: #006666;'&gt;4&lt;/span&gt;,&lt;span style='color: #006666;'&gt;4&lt;/span&gt;,&lt;span style='color: #006666;'&gt;11&lt;/span&gt;,&lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;year&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;yday&lt;/span&gt; &lt;br/&gt;    &lt;span style='color: #0000FF; font-weight: bold;'&gt;true&lt;/span&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;def&lt;/span&gt; christmas_day?&lt;br/&gt;    &lt;span style='color: #0000FF; font-weight: bold;'&gt;return&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;false&lt;/span&gt; &lt;span style='color: #9966CC; font-weight: bold;'&gt;unless&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;month&lt;/span&gt; == &lt;span style='color: #006666;'&gt;12&lt;/span&gt; &amp;amp;&amp;amp; &lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;mday&lt;/span&gt; == &lt;span style='color: #006666;'&gt;25&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #0000FF; font-weight: bold;'&gt;true&lt;/span&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;&lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/pre&gt;&lt;h3&gt;I see only trees.  Where is the forest?&lt;/h3&gt;
&lt;p&gt;What we're worried about is &lt;strong&gt;how&lt;/strong&gt; Time was extended.  It was a simple class_eval -- just open the class up and insert these methods.  The file is simply required in config/environment.rb.  In this case, I am adding functionality that didn't exist, so it could be left alone.  It's not that big of a deal.  As long as you're looking at the file that contains this code, understanding what is happening is pretty simple.&lt;/p&gt;
&lt;p&gt;I would like to move these methods to a module.  Time could then include/extend them.  I do like to have modules in the lib/ directory while using initializers to pull them into DRA.  That method of organization seems much cleaner to me.  Plus, I can't very well end this article by telling you that I'm just going to leave DRA alone.  Not now that we've come this far.  &lt;/p&gt;
&lt;h3&gt;Modularize It Already!&lt;/h3&gt;
&lt;pre&gt;
&lt;div class='codeblock'&gt;&lt;div class='ruby' style='font-family: monospace;'&gt;&lt;span style='color: #9966CC; font-weight: bold;'&gt;module&lt;/span&gt; NframeTimeExtensions&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;module&lt;/span&gt; ClassMethods&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;def&lt;/span&gt; next_sunday&lt;br/&gt;      t = Time.&lt;span style='color: #9900CC;'&gt;local&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;Time.&lt;span style='color: #9900CC;'&gt;now&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;year&lt;/span&gt;, Time.&lt;span style='color: #9900CC;'&gt;now&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;month&lt;/span&gt;, Time.&lt;span style='color: #9900CC;'&gt;now&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;day&lt;/span&gt;, &lt;span style='color: #006666;'&gt;0&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt; &lt;span style='color: #008000; font-style: italic;'&gt;# Today at midnight&lt;/span&gt;&lt;br/&gt;      t += &lt;span style='color: #006666;'&gt;60&lt;/span&gt;*&lt;span style='color: #006666;'&gt;60&lt;/span&gt;*&lt;span style='color: #006666;'&gt;24&lt;/span&gt; &lt;span style='color: #9966CC; font-weight: bold;'&gt;until&lt;/span&gt; t.&lt;span style='color: #9900CC;'&gt;wday&lt;/span&gt; == &lt;span style='color: #006666;'&gt;0&lt;/span&gt;&lt;br/&gt;      t &lt;span style='color: #008000; font-style: italic;'&gt;# Should now be next Sunday at midnight&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;def&lt;/span&gt; nth_wday&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;n, wday, month, year&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;&lt;br/&gt;      &lt;span style='color: #9966CC; font-weight: bold;'&gt;if&lt;/span&gt; &lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;!n.&lt;span style='color: #9900CC;'&gt;between&lt;/span&gt;? &lt;span style='color: #006666;'&gt;1&lt;/span&gt;,&lt;span style='color: #006666;'&gt;5&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt; ||&lt;br/&gt;         &lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;!wday.&lt;span style='color: #9900CC;'&gt;between&lt;/span&gt;? &lt;span style='color: #006666;'&gt;0&lt;/span&gt;,&lt;span style='color: #006666;'&gt;6&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt; ||&lt;br/&gt;         &lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;!month.&lt;span style='color: #9900CC;'&gt;between&lt;/span&gt;? &lt;span style='color: #006666;'&gt;1&lt;/span&gt;,&lt;span style='color: #006666;'&gt;12&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;&lt;br/&gt;        &lt;span style='color: #CC0066; font-weight: bold;'&gt;raise&lt;/span&gt; ArgumentError&lt;br/&gt;      &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;      t = Time.&lt;span style='color: #9900CC;'&gt;local&lt;/span&gt; year, month, &lt;span style='color: #006666;'&gt;1&lt;/span&gt;&lt;br/&gt;      first = t.&lt;span style='color: #9900CC;'&gt;wday&lt;/span&gt;&lt;br/&gt;      &lt;span style='color: #9966CC; font-weight: bold;'&gt;if&lt;/span&gt; first == wday&lt;br/&gt;        fwd = &lt;span style='color: #006666;'&gt;1&lt;/span&gt;&lt;br/&gt;      &lt;span style='color: #9966CC; font-weight: bold;'&gt;elsif&lt;/span&gt; first &amp;lt; wday&lt;br/&gt;        fwd = wday - first + &lt;span style='color: #006666;'&gt;1&lt;/span&gt;&lt;br/&gt;      &lt;span style='color: #9966CC; font-weight: bold;'&gt;elsif&lt;/span&gt; first &amp;gt; wday&lt;br/&gt;        fwd = &lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;wday&lt;span style='color: #006666;'&gt;+7&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt; - first + &lt;span style='color: #006666;'&gt;1&lt;/span&gt;&lt;br/&gt;      &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;      target = fwd + &lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;n&lt;span style='color: #006666;'&gt;-1&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;*&lt;span style='color: #006666;'&gt;7&lt;/span&gt;&lt;br/&gt;      &lt;span style='color: #9966CC; font-weight: bold;'&gt;begin&lt;/span&gt;&lt;br/&gt;        t2 = Time.&lt;span style='color: #9900CC;'&gt;local&lt;/span&gt; year, month, target&lt;br/&gt;      &lt;span style='color: #9966CC; font-weight: bold;'&gt;rescue&lt;/span&gt; ArgumentError&lt;br/&gt;        &lt;span style='color: #0000FF; font-weight: bold;'&gt;return&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;nil&lt;/span&gt;&lt;br/&gt;      &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;      &lt;span style='color: #9966CC; font-weight: bold;'&gt;if&lt;/span&gt; t2.&lt;span style='color: #9900CC;'&gt;mday&lt;/span&gt; == target&lt;br/&gt;        t2&lt;br/&gt;      &lt;span style='color: #9966CC; font-weight: bold;'&gt;else&lt;/span&gt;&lt;br/&gt;        &lt;span style='color: #0000FF; font-weight: bold;'&gt;nil&lt;/span&gt;&lt;br/&gt;      &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;def&lt;/span&gt; after_hours?&lt;br/&gt;    result = &lt;span style='color: #0000FF; font-weight: bold;'&gt;true&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #008000; font-style: italic;'&gt;# M-F, 8am to 6pm are business hours&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;if&lt;/span&gt; &lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;&lt;span style='color: #006666;'&gt;1&lt;/span&gt;..&lt;span style='color: #006666;'&gt;5&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;.&lt;span style='color: #9966CC; font-weight: bold;'&gt;include&lt;/span&gt;?&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;&lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;wday&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt; &amp;amp;&amp;amp; &lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;&lt;span style='color: #006666;'&gt;8&lt;/span&gt;..&lt;span style='color: #006666;'&gt;18&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;.&lt;span style='color: #9966CC; font-weight: bold;'&gt;include&lt;/span&gt;?&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;&lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;hour&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt; &lt;br/&gt;      result = &lt;span style='color: #0000FF; font-weight: bold;'&gt;false&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #008000; font-style: italic;'&gt;# unless it's a holiday!&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;if&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;holiday&lt;/span&gt;?&lt;br/&gt;      result = &lt;span style='color: #0000FF; font-weight: bold;'&gt;true&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;    result&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;def&lt;/span&gt; holiday?&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;unless&lt;/span&gt; new_years_day? || &lt;br/&gt;       mlk_jr_day? ||&lt;br/&gt;       memorial_day? ||&lt;br/&gt;       independence_day? ||&lt;br/&gt;       labor_day? ||&lt;br/&gt;       thanksgiving? ||&lt;br/&gt;       christmas_day?&lt;br/&gt;      &lt;span style='color: #0000FF; font-weight: bold;'&gt;return&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;false&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #0000FF; font-weight: bold;'&gt;true&lt;/span&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;def&lt;/span&gt; new_years_day?&lt;br/&gt;    &lt;span style='color: #0000FF; font-weight: bold;'&gt;return&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;false&lt;/span&gt; &lt;span style='color: #9966CC; font-weight: bold;'&gt;unless&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;yday&lt;/span&gt; == &lt;span style='color: #006666;'&gt;1&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #0000FF; font-weight: bold;'&gt;true&lt;/span&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;def&lt;/span&gt; mlk_jr_day?&lt;br/&gt;    &lt;span style='color: #0000FF; font-weight: bold;'&gt;return&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;false&lt;/span&gt; &lt;span style='color: #9966CC; font-weight: bold;'&gt;unless&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;yday&lt;/span&gt; == Time.&lt;span style='color: #9900CC;'&gt;nth_wday&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;&lt;span style='color: #006666;'&gt;3&lt;/span&gt;,&lt;span style='color: #006666;'&gt;1&lt;/span&gt;,&lt;span style='color: #006666;'&gt;1&lt;/span&gt;,&lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;year&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #0000FF; font-weight: bold;'&gt;true&lt;/span&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;def&lt;/span&gt; memorial_day?&lt;br/&gt;    last_day_of_month = Time.&lt;span style='color: #9900CC;'&gt;local&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;&lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;year&lt;/span&gt;, &lt;span style='color: #006666;'&gt;5&lt;/span&gt;, &lt;span style='color: #006666;'&gt;31&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;&lt;br/&gt;    d = last_day_of_month.&lt;span style='color: #9900CC;'&gt;mday&lt;/span&gt; - last_day_of_month.&lt;span style='color: #9900CC;'&gt;wday&lt;/span&gt; + &lt;span style='color: #006666;'&gt;1&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #0000FF; font-weight: bold;'&gt;return&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;false&lt;/span&gt; &lt;span style='color: #9966CC; font-weight: bold;'&gt;unless&lt;/span&gt; Time.&lt;span style='color: #9900CC;'&gt;now&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;yday&lt;/span&gt; == Time.&lt;span style='color: #9900CC;'&gt;local&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;&lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;year&lt;/span&gt;, &lt;span style='color: #006666;'&gt;5&lt;/span&gt;, d&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #0000FF; font-weight: bold;'&gt;true&lt;/span&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;def&lt;/span&gt; indepence_day?&lt;br/&gt;    &lt;span style='color: #0000FF; font-weight: bold;'&gt;return&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;false&lt;/span&gt; &lt;span style='color: #9966CC; font-weight: bold;'&gt;unless&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;month&lt;/span&gt; == &lt;span style='color: #006666;'&gt;7&lt;/span&gt; &amp;amp;&amp;amp; &lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;mday&lt;/span&gt; == &lt;span style='color: #006666;'&gt;4&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #0000FF; font-weight: bold;'&gt;true&lt;/span&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;def&lt;/span&gt; labor_day?&lt;br/&gt;    &lt;span style='color: #0000FF; font-weight: bold;'&gt;return&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;false&lt;/span&gt; &lt;span style='color: #9966CC; font-weight: bold;'&gt;unless&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;yday&lt;/span&gt; == Time.&lt;span style='color: #9900CC;'&gt;nth_wday&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;&lt;span style='color: #006666;'&gt;1&lt;/span&gt;,&lt;span style='color: #006666;'&gt;1&lt;/span&gt;,&lt;span style='color: #006666;'&gt;9&lt;/span&gt;,&lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;year&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;yday&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #0000FF; font-weight: bold;'&gt;true&lt;/span&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;def&lt;/span&gt; thanksgiving?&lt;br/&gt;    &lt;span style='color: #0000FF; font-weight: bold;'&gt;return&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;false&lt;/span&gt; &lt;span style='color: #9966CC; font-weight: bold;'&gt;unless&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;yday&lt;/span&gt; == &lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9966CC; font-weight: bold;'&gt;class&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;nth_wday&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;(&lt;/span&gt;&lt;span style='color: #006666;'&gt;4&lt;/span&gt;,&lt;span style='color: #006666;'&gt;4&lt;/span&gt;,&lt;span style='color: #006666;'&gt;11&lt;/span&gt;,&lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;year&lt;/span&gt;&lt;span style='color: #006600; font-weight: bold;'&gt;)&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;yday&lt;/span&gt; &lt;br/&gt;    &lt;span style='color: #0000FF; font-weight: bold;'&gt;true&lt;/span&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;def&lt;/span&gt; christmas_day?&lt;br/&gt;    &lt;span style='color: #0000FF; font-weight: bold;'&gt;return&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;false&lt;/span&gt; &lt;span style='color: #9966CC; font-weight: bold;'&gt;unless&lt;/span&gt; &lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;month&lt;/span&gt; == &lt;span style='color: #006666;'&gt;12&lt;/span&gt; &amp;amp;&amp;amp; &lt;span style='color: #0000FF; font-weight: bold;'&gt;self&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;mday&lt;/span&gt; == &lt;span style='color: #006666;'&gt;25&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #0000FF; font-weight: bold;'&gt;true&lt;/span&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;br/&gt;&lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/pre&gt;&lt;p&gt;Ah, there.  The module ClassMethods pattern is used throughout Rails, so you've likely seen it before.  Other than that, it's pretty straight-forward.  I hope.&lt;/p&gt;
&lt;p&gt;Now I need to get it into DRA.  So, in RAILS&lt;em&gt;ROOT/config/initializers/hey&lt;/em&gt;lets&lt;em&gt;include&lt;/em&gt;those&lt;em&gt;stupid&lt;/em&gt;time_extensions.rb:&lt;/p&gt;
&lt;pre&gt;
&lt;div class='codeblock'&gt;&lt;div class='ruby' style='font-family: monospace;'&gt;&lt;span style='color: #CC0066; font-weight: bold;'&gt;require&lt;/span&gt; 'nframe_time_extensions'&lt;br/&gt;&lt;br/&gt;Time.&lt;span style='color: #9900CC;'&gt;class_eval&lt;/span&gt; &lt;span style='color: #9966CC; font-weight: bold;'&gt;do&lt;/span&gt;&lt;br/&gt;  &lt;span style='color: #9966CC; font-weight: bold;'&gt;include&lt;/span&gt; NframeTimeExtensions&lt;br/&gt;  extend NframeTimeExtensions::ClassMethods&lt;br/&gt;&lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/pre&gt;&lt;p&gt;That smells better to me.&lt;/p&gt;
&lt;h3&gt;Whoa.  Way More Code.&lt;/h3&gt;
&lt;p&gt;Yeah, this does take more code, and an extra file.  I don't mind writing a bit of extra code when I believe that it will make the code clearer.  As I said before, this method of organization seems much clearer to me.  &lt;/p&gt;
&lt;p&gt;It was also a fun little exercise in extending via modules instead of directly adding to classes.  That is not a bad thing to practice.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Re-Thinking Hack-Svn &amp;&amp; Ship-Svn</title>
    <link href="/blog/2008/09/09/re-thinking-hack-svn-andand-ship-svn.html" />
    <id>tag:mileszs.com,2008-09-09:1220980959</id>
    <updated>2008-09-09T17:22:39Z</updated>
    <content type="html">&lt;p&gt;In a comment to &lt;a href='http://soyunperdedor.com/node/31'&gt;Hack-svn &amp;amp;&amp;amp; Ship-svn&lt;/a&gt;, &lt;a href='http://bl.ogtastic.com/'&gt;Yossef Mendelssohn of OG Consulting&lt;/a&gt;, suggested that my chosen workflow may not be ideal.  He probably knows much better than I.  Here's the (relevent portion of the) comment:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Also, about your git-svn scripts. Kevin (the third OG) was working on the same thing, even going so far as to make 'hack' and 'ship' DTRT based on whether you're in a git-svn repo or a regular git repo. In my experience, however, there's more difference between vanilla git and git-svn than just whether you git pull or git svn rebase. I've had enough troubles that I no longer dcommit from master. I always keep master as the state of the svn repo and dcommit from branches. Much fewer headaches that way.&lt;/p&gt;
&lt;p/&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;(Note that the three OGs are Yossef Mendelssohn, Rick Bradley, and Kevin Barnes.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I had previously seen a similar idea suggested by a commentor to Andy Delcambre's &lt;a href='http://andy.delcambre.com/2008/03/04/git-svn-workflow.html'&gt;'Git SVN Workflow'&lt;/a&gt; by the name of Chris McGrath:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;That workflow is very similar to how I use git-svn. Something I don’t do that I’ve seen in nearly every article like this is the merge back to master. I just git-svn rebase my topic branch to svn HEAD, fix any conflicts there and git-svn dcommit that, possibly doing a rebase -i beforehand to clean up the commit(s). Then I switch to master, rebase again to get the committed changes and delete the topic branch. My reasons for this aren’t really biggies imho, more that this is the way I’ve always done it. They are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Master will always contain only changes that are in svn, so easy to make a quick bugfix branch without having to work out which rev to branch from. 2. The git-svn man page doesn’t recommend doing git merges, but using dcommit or format patch to move changes between git branches. 3. It’s one less place to get conflicts, as they will only happen on the topic branch rebase. ...&lt;/li&gt;
&lt;/ol&gt;
&lt;p/&gt;&lt;/blockquote&gt;
&lt;p&gt;Fair enough.&lt;/p&gt;
&lt;h3&gt;So, that means what?&lt;/h3&gt;
&lt;p&gt;Well, following the basics of Yossef's suggestion would make ship-svn really, really boring.  (That's not to say it is incorrect, or ineffective.)  &lt;/p&gt;
&lt;pre&gt;
&lt;div class='codeblock'&gt;&lt;div class='bash' style='font-family: monospace;'&gt;&lt;span style='color: #808080; font-style: italic;'&gt;#!/bin/sh -x&lt;br/&gt;&lt;/span&gt;&lt;span style='color: #0000ff;'&gt;CURRENT=&lt;/span&gt;`git name-rev HEAD --name-only`&lt;br/&gt;git svn dcommit&lt;/div&gt;&lt;/div&gt;
&lt;/pre&gt;&lt;p&gt;Right?  Should we also checkout master and 'git svn rebase' again?  That step would get done the next time you run 'hack-svn' anyway.  Hmm.  Maybe there is another, better workflow of which I am unaware?   &lt;/p&gt;
&lt;p&gt;Mr. McGrath is suggesting something like:&lt;/p&gt;
&lt;pre&gt;
&lt;div class='codeblock'&gt;&lt;div class='bash' style='font-family: monospace;'&gt;&lt;span style='color: #808080; font-style: italic;'&gt;#!/bin/sh -x&lt;br/&gt;&lt;/span&gt;&lt;span style='color: #0000ff;'&gt;CURRENT=&lt;/span&gt;`git name-rev HEAD --name-only`&lt;br/&gt;git svn rebase&lt;br/&gt;git svn dcommit&lt;br/&gt;git checkout master&lt;br/&gt;git svn rebase&lt;br/&gt;git branch -D &lt;span style='color: #0000ff;'&gt;$&lt;span style='color: #66cc66;'&gt;{&lt;/span&gt;CURRENT&lt;span style='color: #66cc66;'&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/pre&gt;&lt;p&gt;I don't really like that.  However, I do have a nasty habit of working out of the same 'topic' branch constantly, rather than naming a new one with each new feature.  Sometimes I do feel the need to have a few different branches active, if I am unsure of the best path, and I intend to discover it through experimentation.  Most of the time, I just work out of 'devel'.  That approach makes the adaptation of the original Hack &amp;amp;&amp;amp; Ship method pleasing, to me.&lt;/p&gt;
&lt;p&gt;Again, that probably means I'm wrong.  I will likely adopt my interpretation of Yossef's method soon, while sticking with the original for now.  I'd like to think about it for a bit.  I would also enjoy hearing what others think.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Hack-Svn &amp;&amp; Ship-Svn</title>
    <link href="/blog/2008/08/28/hack-svn-andand-ship-svn.html" />
    <id>tag:mileszs.com,2008-08-28:1219958178</id>
    <updated>2008-08-28T21:16:18Z</updated>
    <content type="html">&lt;p&gt;I was inspired today by &lt;a href='http://reinh.com/blog/2008/08/27/hack-and-and-ship.html' title='Rein Henrich&amp;apos;s Hack &amp;amp;&amp;amp; Ship post'&gt;Rein's Hack &amp;amp;&amp;amp; Ship post&lt;/a&gt; to write something similar for those of us stuck in the &lt;strong&gt;Dark Ages of Git-SVN&lt;/strong&gt;.  It took very little work on my part.  Honestly, the originals did most of the work.  I changed &lt;em&gt;two lines&lt;/em&gt;.  &lt;/p&gt;
&lt;p&gt;What I'm trying to say is: All praise be to the original author, Rick Bradley, and to Rein for exposing them to the &lt;strong&gt;Rest of Us&lt;/strong&gt;.  &lt;strong&gt;&lt;em&gt;Edit:&lt;/em&gt;&lt;/strong&gt;Thanks also to Yossef Mendelssohn for the idea and his tips in the comments section, and to Kevin Barnes, because he's part of that crew surely had a hand in it -- and he's working on a sweet related script.  Again, see Yossef's comment.&lt;/p&gt;
&lt;h3&gt;Hack-svn &amp;amp;&amp;amp; Ship-svn: The Scripts&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Hack-svn&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;
&lt;div class='codeblock'&gt;&lt;div class='bash' style='font-family: monospace;'&gt;&lt;span style='color: #808080; font-style: italic;'&gt;#!/bin/sh -x&lt;br/&gt;&lt;/span&gt;&lt;span style='color: #0000ff;'&gt;CURRENT=&lt;/span&gt;`git name-rev HEAD --name-only`&lt;br/&gt;git checkout master&lt;br/&gt;git svn rebase&lt;br/&gt;git checkout &lt;span style='color: #0000ff;'&gt;$&lt;span style='color: #66cc66;'&gt;{&lt;/span&gt;CURRENT&lt;span style='color: #66cc66;'&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br/&gt;git rebase master&lt;/div&gt;&lt;/div&gt;
&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Ship-svn&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;
&lt;div class='codeblock'&gt;&lt;div class='bash' style='font-family: monospace;'&gt;&lt;span style='color: #808080; font-style: italic;'&gt;#!/bin/sh -x&lt;br/&gt;&lt;/span&gt;&lt;span style='color: #0000ff;'&gt;CURRENT=&lt;/span&gt;`git name-rev HEAD --name-only`&lt;br/&gt;git checkout master&lt;br/&gt;git merge &lt;span style='color: #0000ff;'&gt;$&lt;span style='color: #66cc66;'&gt;{&lt;/span&gt;CURRENT&lt;span style='color: #66cc66;'&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br/&gt;git svn dcommit&lt;br/&gt;git checkout &lt;span style='color: #0000ff;'&gt;$&lt;span style='color: #66cc66;'&gt;{&lt;/span&gt;CURRENT&lt;span style='color: #66cc66;'&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/pre&gt;&lt;h3&gt;You really are un perdedor&lt;/h3&gt;
&lt;p&gt;As you can see, I indeed only changed two lines to make them work for me.  The names 'hack-svn' and 'ship-svn' are not nearly as fun as 'hack' and 'ship', whether reading them aloud, or typing them after completing a feature.  However, I want to also have vanilla-git hack and ship available for vanilla-git projects.  The name at least makes their function semi-obvious, if you know about hack &amp;amp;&amp;amp; ship.  I thought about just calling them 'hacks' and 'ships', giving them a bit of a Paris Hilton feel.  Loves it?&lt;/p&gt;
&lt;p&gt;Yes, I obsess over what to call scripts I write.  It's a disease.  When I recover, I'll start some sort of rehab clinic bearing my name.  &lt;/p&gt;
&lt;p&gt;So, have at it, my fellow git-svn-ers.  You might also write a local alias for git svn rebase and git svn dcommit to make you feel like you aren't really using subversion.  It's therepeutic.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Rails 2.1 Time Zone Trouble</title>
    <link href="/blog/2008/08/10/rails-2.1-time-zone-trouble.html" />
    <id>tag:mileszs.com,2008-08-10:1218404507</id>
    <updated>2008-08-10T21:41:47Z</updated>
    <content type="html">&lt;p&gt;Making an app deal with multiple time zones is easy in Rails 2.1.  Right?  Just include a default time zone your environment.rb:&lt;/p&gt;
&lt;pre&gt;
&lt;div class='codeblock'&gt;&lt;div class='ruby' style='font-family: monospace;'&gt;Rails::Initializer.&lt;span style='color: #9900CC;'&gt;run&lt;/span&gt; &lt;span style='color: #9966CC; font-weight: bold;'&gt;do&lt;/span&gt; |config|&lt;br/&gt;&lt;span style='color: #008000; font-style: italic;'&gt;# ...&lt;/span&gt;&lt;br/&gt;  config.&lt;span style='color: #9900CC;'&gt;time_zone&lt;/span&gt; = &lt;span style='color: #996600;'&gt;"Eastern Time (US &amp;amp; Canada)"&lt;/span&gt;&lt;br/&gt;&lt;span style='color: #008000; font-style: italic;'&gt;# ...&lt;/span&gt;&lt;br/&gt;&lt;span style='color: #9966CC; font-weight: bold;'&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/pre&gt;&lt;p&gt;Rails will store time stamps in UTC in the database.  They will be converted to the default time zone when they are requested.  I thought that sounded great! Although, at work, we don't currently need to support any other time zone than Eastern, there are a few reasons we suspect we may need to do so sooner than later.  We welcomed the time zone support in Rails 2.1.  Unfortunately, it didn't work out all that well for us, and we don't know why. &lt;/p&gt;
&lt;p&gt;Basically, Rails isn't converting the times correctly.  System time on the server is correct, so there has to be something wrong withour Rails config(s).  The best way to illustrate the problem is with irb:&lt;/p&gt;
&lt;pre&gt;
&lt;div class='codeblock'&gt;&lt;div class='ruby' style='font-family: monospace;'&gt;&lt;span style='color: #008000; font-style: italic;'&gt;# A newly created object&lt;/span&gt;&lt;br/&gt;$&amp;gt;script/console&lt;br/&gt;&amp;gt;&amp;gt; t = Ticket.&lt;span style='color: #9900CC;'&gt;last&lt;/span&gt;&lt;br/&gt;&amp;gt;&amp;gt; t.&lt;span style='color: #9900CC;'&gt;created_at&lt;/span&gt;&lt;br/&gt;=&amp;gt; Tue, &lt;span style='color: #006666;'&gt;05&lt;/span&gt; Aug &lt;span style='color: #006666;'&gt;2008&lt;/span&gt; &lt;span style='color: #006666;'&gt;13&lt;/span&gt;:&lt;span style='color: #006666;'&gt;24&lt;/span&gt;:&lt;span style='color: #006666;'&gt;56&lt;/span&gt; EDT &lt;span style='color: #006666;'&gt;-04&lt;/span&gt;:&lt;span style='color: #006666;'&gt;00&lt;/span&gt; &lt;span style='color: #008000; font-style: italic;'&gt;# NOT correct -- it should be 9:24 EDT -04:00&lt;/span&gt;&lt;br/&gt;&amp;gt;&amp;gt; t.&lt;span style='color: #9900CC;'&gt;created_at_before_type_cast&lt;/span&gt;&lt;br/&gt;=&amp;gt; Tue, &lt;span style='color: #006666;'&gt;05&lt;/span&gt; Aug &lt;span style='color: #006666;'&gt;2008&lt;/span&gt; &lt;span style='color: #006666;'&gt;13&lt;/span&gt;:&lt;span style='color: #006666;'&gt;24&lt;/span&gt;:&lt;span style='color: #006666;'&gt;56&lt;/span&gt; &lt;span style='color: #006666;'&gt;+0000&lt;/span&gt; &lt;span style='color: #008000; font-style: italic;'&gt;# Correct -- UTC&lt;/span&gt;&lt;br/&gt;&amp;gt;&amp;gt; Time.&lt;span style='color: #9900CC;'&gt;now&lt;/span&gt;&lt;br/&gt;=&amp;gt; Tue Aug &lt;span style='color: #006666;'&gt;05&lt;/span&gt; &lt;span style='color: #006666;'&gt;09&lt;/span&gt;:&lt;span style='color: #006666;'&gt;25&lt;/span&gt;:&lt;span style='color: #006666;'&gt;34&lt;/span&gt; &lt;span style='color: #006666;'&gt;-0400&lt;/span&gt; &lt;span style='color: #006666;'&gt;2008&lt;/span&gt; &lt;span style='color: #008000; font-style: italic;'&gt;# Correct&lt;/span&gt;&lt;br/&gt;&amp;gt;&amp;gt; Time.&lt;span style='color: #9900CC;'&gt;zone&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;now&lt;/span&gt;&lt;br/&gt;=&amp;gt; Tue, &lt;span style='color: #006666;'&gt;05&lt;/span&gt; Aug &lt;span style='color: #006666;'&gt;2008&lt;/span&gt; &lt;span style='color: #006666;'&gt;09&lt;/span&gt;:&lt;span style='color: #006666;'&gt;26&lt;/span&gt;:&lt;span style='color: #006666;'&gt;11&lt;/span&gt; EDT &lt;span style='color: #006666;'&gt;-04&lt;/span&gt;:&lt;span style='color: #006666;'&gt;00&lt;/span&gt; &lt;span style='color: #008000; font-style: italic;'&gt;# Correct&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/pre&gt;&lt;p&gt;So, the only thing that is not correct is the conversion from UTC in the database to local time.  It seems to think that local time and UTC should be the same, and only changes the offset, and not the actual time.  I posed this issue to #rubyonrails.  Possibly the most accurate response was, "Something is f'd up."&lt;/p&gt;
&lt;p&gt;So, would you like to see what happens when we tell Rails we want to default to Pacific Time (US &amp;amp; Canada), just for fun?  &lt;/p&gt;
&lt;pre&gt;
&lt;div class='codeblock'&gt;&lt;div class='ruby' style='font-family: monospace;'&gt;&lt;span style='color: #008000; font-style: italic;'&gt;# If config.time_zone is set to "Pacific Time (US &amp;amp; Canada)"&lt;/span&gt;&lt;br/&gt;&amp;gt;&amp;gt; t.&lt;span style='color: #9900CC;'&gt;created_at&lt;/span&gt;&lt;br/&gt;=&amp;gt; Tue, &lt;span style='color: #006666;'&gt;05&lt;/span&gt; Aug &lt;span style='color: #006666;'&gt;2008&lt;/span&gt; &lt;span style='color: #006666;'&gt;10&lt;/span&gt;:&lt;span style='color: #006666;'&gt;38&lt;/span&gt;:&lt;span style='color: #006666;'&gt;55&lt;/span&gt; PDT &lt;span style='color: #006666;'&gt;-07&lt;/span&gt;:&lt;span style='color: #006666;'&gt;00&lt;/span&gt; &lt;span style='color: #008000; font-style: italic;'&gt;# NOT correct -- it's 6:38 PDT -07:00&lt;/span&gt;&lt;br/&gt;&amp;gt;&amp;gt; t.&lt;span style='color: #9900CC;'&gt;created_at_before_type_cast&lt;/span&gt;&lt;br/&gt;=&amp;gt; Tue, &lt;span style='color: #006666;'&gt;05&lt;/span&gt; Aug &lt;span style='color: #006666;'&gt;2008&lt;/span&gt; &lt;span style='color: #006666;'&gt;13&lt;/span&gt;:&lt;span style='color: #006666;'&gt;38&lt;/span&gt;:&lt;span style='color: #006666;'&gt;55&lt;/span&gt; &lt;span style='color: #006666;'&gt;+0000&lt;/span&gt; &lt;span style='color: #008000; font-style: italic;'&gt;# Correct -- UTC&lt;/span&gt;&lt;br/&gt;&amp;gt;&amp;gt; Time.&lt;span style='color: #9900CC;'&gt;now&lt;/span&gt;&lt;br/&gt;=&amp;gt; Tue Aug &lt;span style='color: #006666;'&gt;05&lt;/span&gt; &lt;span style='color: #006666;'&gt;09&lt;/span&gt;:&lt;span style='color: #006666;'&gt;40&lt;/span&gt;:&lt;span style='color: #006666;'&gt;02&lt;/span&gt; &lt;span style='color: #006666;'&gt;-0400&lt;/span&gt; &lt;span style='color: #006666;'&gt;2008&lt;/span&gt; &lt;span style='color: #008000; font-style: italic;'&gt;# Correct for my local time&lt;/span&gt;&lt;br/&gt;&amp;gt;&amp;gt; Time.&lt;span style='color: #9900CC;'&gt;zone&lt;/span&gt;.&lt;span style='color: #9900CC;'&gt;now&lt;/span&gt;&lt;br/&gt;=&amp;gt; Tue, &lt;span style='color: #006666;'&gt;05&lt;/span&gt; Aug &lt;span style='color: #006666;'&gt;2008&lt;/span&gt; &lt;span style='color: #006666;'&gt;06&lt;/span&gt;:&lt;span style='color: #006666;'&gt;39&lt;/span&gt;:&lt;span style='color: #006666;'&gt;57&lt;/span&gt; PDT &lt;span style='color: #006666;'&gt;-07&lt;/span&gt;:&lt;span style='color: #006666;'&gt;00&lt;/span&gt; &lt;span style='color: #008000; font-style: italic;'&gt;# Correct&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/pre&gt;&lt;p&gt;What!?  Rails thinks that EDT == UTC, and the difference between EDT and PDT is 3 hours, so just subtract 3 hours from whatever we get back from the database.&lt;br/&gt;
F'd up indeed!&lt;/p&gt;
&lt;p&gt;I gave up.  I may have given up too early.  Perhaps I could have figured it out.  Frankly, there were better things to do.  We just store everything in local time.  For now, it won't hurt anything.  In the future, it could be quite a hindrance.&lt;/p&gt;
&lt;p&gt;Do you have any idea what is wrong?  I'll buy you a beer if you find a solution.   &lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Too Many Choices</title>
    <link href="/blog/2008/07/10/too-many-choices.html" />
    <id>tag:mileszs.com,2008-07-10:1215654850</id>
    <updated>2008-07-10T01:54:10Z</updated>
    <content type="html">&lt;p&gt;&lt;img title='I love my nerd glasses!' src='http://soyunperdedor.com/files/nerd_glasses.jpg' alt='Stack of Logs'/&gt;&lt;br/&gt;
&lt;a href='http://www.flickr.com/photos/world9-1/'&gt;Photo By Tomotaka&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I have spent a silly amount of time lately contemplating what I want in a&lt;br/&gt;
blogging engine/system/platform/thing.  I know one thing: Drupal, the system I&lt;br/&gt;
am currently using, is not it.  It's a fine content management system.  In&lt;br/&gt;
fact, I use it as a content management system for another site, and I am quite&lt;br/&gt;
happy with it.  However, it's &lt;strong&gt;much&lt;/strong&gt; more than what I need.  I mean,&lt;br/&gt;
honestly, I inconsistently publish a few thoughts with some code.  I do NOT&lt;br/&gt;
need a content management system.  So, what do I need?&lt;/p&gt;
&lt;h3&gt;WordPress ... ?&lt;/h3&gt;
&lt;p&gt;Woo!  WordPress!  It's awesome, isn't it?  Everyone uses it, so it must be&lt;br/&gt;
good.  I have been inside of it, and it seemed nice.  It was pretty.  It has a&lt;br/&gt;
plugin for everything, I hear.  I know that a lot of people use it as their&lt;br/&gt;
content management system.  Wow! &lt;/p&gt;
&lt;p&gt;Therein lies the problem.  WordPress is &lt;em&gt;just&lt;/em&gt; big enough to be used as a&lt;br/&gt;
content management system.  It does just enough to allow some smaller sites to&lt;br/&gt;
utlitize it to manage their whole site.  When I look at WordPress, it still&lt;br/&gt;
seems bigger than my needs.  It does too much.  It's got a great community.&lt;br/&gt;
I'm sure I could make it do less, or make it do more.  It would probably work&lt;br/&gt;
satisfactorily.  I don't want to be satisfied, though.  I want to be stoked!&lt;br/&gt;
So, forget WordPress.  &lt;/p&gt;
&lt;h3&gt;Static Blogging Engines&lt;/h3&gt;
&lt;p&gt;I am a &lt;strong&gt;huge freakin' nerd&lt;/strong&gt;.  You know what I want?  I want to generate all&lt;br/&gt;
of my blog posts via the command line.  I think I'd like to run &lt;code&gt;rake blog:post FILE=some_file.mkd&lt;/code&gt; to post a new article.  I think I&lt;br/&gt;
would like my blog to be completely managed from the command line... or, at&lt;br/&gt;
least, mostly managed from the command line.&lt;/p&gt;
&lt;p&gt;I am progressing to this end already.  I write my posts in Vim using Markdown&lt;br/&gt;
syntax.  Next, I run &lt;code&gt;markdown blog_post_file_name.mkd&lt;/code&gt;.&lt;br/&gt;
I copy the output, and paste it into the text box in the administration portion&lt;br/&gt;
of my blog.  I really would be happy doing it all from the command line.  On&lt;br/&gt;
top of all this command-line geekery, the static XHTML pages -- generated from&lt;br/&gt;
such a system -- tend to be fast.  Although I don't expect me to ever say&lt;br/&gt;
anything so spectacular, were I to ever get SlashDotted or Digged or Reddited&lt;br/&gt;
or even HackerNewsed, I would not need worry.  That is comforting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;:Having not used the following options extensively to create an actual&lt;br/&gt;
blog, it feels a bit dirty to criticize, but I'm going to do it anyway.  You've&lt;br/&gt;
been warned!&lt;/p&gt;
&lt;p&gt;&lt;a href='http://hobix.com/' title='Hobix Static Blog Engine'&gt;Hobix&lt;/a&gt; is sort of the big name in static blogging, as far as&lt;br/&gt;
Ruby-based systems go.  Hobix was initially written by Why the Luck Stiff, and&lt;br/&gt;
powers RedHanded (his abandoned Ruby coding website).  Hobix is not too heavily&lt;br/&gt;
developed any longer, from what I can tell.  Following the instructions on&lt;br/&gt;
hobix.com for installing Hobix and getting started resulted in an error.  It is&lt;br/&gt;
recommended to try the latest development version.  I will.  &lt;/p&gt;
&lt;p&gt;Then there's &lt;a href='http://snk.rubyforge.org/lib/rassmalog/output/index.html' title='Rassmalog Static Blog Engine'&gt;Rassmalog&lt;/a&gt;, which has actually been updated recently.&lt;br/&gt;
(As of this writing, it was last updated June 8th, 2008.  The user guide was&lt;br/&gt;
updated on June 22nd, 2008.  &lt;strong&gt;Wow!&lt;/strong&gt;  Code &lt;strong&gt;and&lt;/strong&gt; documentation updates!&lt;br/&gt;
Very nice!)  It seems to be a well planned solution, and the comprehensive user&lt;br/&gt;
guide is impressive.  It offers, initially, email based comments.  However,&lt;br/&gt;
Rassmalog's site also describes how to use the JS-Kit comment system.  I am&lt;br/&gt;
sure one could use Disqus as well.  More on comments and third party comment&lt;br/&gt;
systems later.  One final thing to note is that Rassmalog source is available&lt;br/&gt;
via a darcs repository.&lt;/p&gt;
&lt;p&gt;Last, but not least, the engine called &lt;a href='http://webby.rubyforge.org/' title='Webby Static Blog Engine'&gt;Webby&lt;/a&gt;.  Not to be confused&lt;br/&gt;
with the Webby awards.  Note that the name makes searching for obscure blogs&lt;br/&gt;
that use Webby difficult, as so many people enjoy blogging about the Webby&lt;br/&gt;
awards.  In any case, Webby has a few features, it appears, that Rassmalog&lt;br/&gt;
doesn't have, such as LaTeX support, and something about Graphviz.  These&lt;br/&gt;
features are also things that I probably wouldn't use.  However, I &lt;em&gt;would&lt;/em&gt;&lt;br/&gt;
potentially use HAML &amp;amp; SASS, as well as Textile, or maybe Markdown, or maybe&lt;br/&gt;
something else.  I'm saying it wouldn't be a &lt;em&gt;bad&lt;/em&gt; thing to have all of these&lt;br/&gt;
options.  (Did I not just get done saying I wanted less options?  I'm all over&lt;br/&gt;
the place here.)  Webby does not have a built in comment system of any sort, but&lt;br/&gt;
people have used Disqus successfully.  I'm sure other systems would work fine&lt;br/&gt;
as well.  Worth mentioning is that &lt;a href='http://github.com/TwP/webby/tree/master' title='Webby on GitHub'&gt;Webby is on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: &lt;br/&gt;
Oh!  I just stumbled upon &lt;a href='http://github.com/elliottcable/git-blog/tree/master' title='git-blog'&gt;git-blog&lt;/a&gt;.  It's so&lt;br/&gt;
geeky, I certainly can't resist at least &lt;em&gt;trying&lt;/em&gt; it, right?  Simple posting.&lt;br/&gt;
Automatic versioning.  Git.  It is definitely worth a look, for geekery's sake.&lt;/p&gt;
&lt;h3&gt;Comments and Comment Systems&lt;/h3&gt;
&lt;p&gt;I have heard some people question whether comments on blogs are necessary.  In&lt;br/&gt;
fact, I can think of at least one well-respected card-carrying Ruby community&lt;br/&gt;
member who eschews blog comments completely.  (I am looking at you, &lt;a href='http://reinh.com/' title='Rein Henrich&amp;apos;s Site/Blog'&gt;Rein&lt;br/&gt;
Henrich&lt;/a&gt;.)  Why?  Perhaps spam became unbearable.  Maybe he just&lt;br/&gt;
didn't want to deal with comments potentially spiraling out of control and&lt;br/&gt;
becoming a 4chan-like dehumanized zone of ridiculousness.  Maybe he gets plenty&lt;br/&gt;
of feedback via IRC, Twitter, et al.  There's nothing &lt;em&gt;wrong&lt;/em&gt; with it.  I do,&lt;br/&gt;
however, disagree strongly.&lt;/p&gt;
&lt;p&gt;Blog comments are, often enough, the best part of a blog post.  How many times&lt;br/&gt;
have you placed a search engine request, visited a blog that seemed to fit your&lt;br/&gt;
keywords well, and found that, indeed, the answer you wanted was actually&lt;br/&gt;
contained in the comments to the original post, rather than in the post itself?&lt;br/&gt;
I love that.  It restores one Link-heart worth of faith in humanity and&lt;br/&gt;
community.  In addition to that, I often learn something from people commenting&lt;br/&gt;
on my blog posts.  I mean, sure, sometimes what I learn is that there's a new&lt;br/&gt;
cream that is guaranteed to make my nights more exciting, but that &lt;strong&gt;is&lt;/strong&gt;&lt;br/&gt;
&lt;em&gt;something&lt;/em&gt;.  What about &lt;a href='http://soyunperdedor.com/node/25' title='More Neato Tools blog post'&gt;this comment&lt;/a&gt; on one of my blog&lt;br/&gt;
posts?  Useful.  Comments are necessary.&lt;/p&gt;
&lt;p&gt;Now that we have established the indispensability of comments, how should they&lt;br/&gt;
be posted?  Disqus has become a popular solution to comments.  Disqus takes&lt;br/&gt;
much of the burden of dealing with spam, and the other responsibilities of&lt;br/&gt;
managing or writing your own comment system.  In exchange?  They have your&lt;br/&gt;
data.  Oh, and your comments are not going to get indexed by, say, the GOOG,&lt;br/&gt;
because they are dynamically placed in your page via some Javascript.  The same&lt;br/&gt;
goes for JS-Kit, and probably others in that niche.  I want that data, and I&lt;br/&gt;
want it indexed.  How are people to find the helpful comments?  I am sure you&lt;br/&gt;
have a retort readily prepared, dying to flow from your fingertips.  Please,&lt;br/&gt;
feel free to leave a comment.  However, the bottom line is that it doesn't sit&lt;br/&gt;
well with me.  So, man-in-the-middle comment system?  Just say no!&lt;/p&gt;
&lt;p&gt;(A couple interesting links related to blogs and comments from people I respect: &lt;a href='http://www.tbray.org/ongoing/When/200x/2006/05/20/Comments-on-Comments'&gt;Tim Bray just before he had a commenting system&lt;/a&gt;, and &lt;a href='http://www.intertwingly.net/blog/2006/05/15/Comments-Please'&gt;Sam Ruby's advice to Tim Bray on a commenting system&lt;/a&gt;.  Enjoy.)&lt;/p&gt;
&lt;p&gt;I will figure out a way to manage my own comments.  This will probably mean comment content ultimately ending up in my inbox before they are made public.  I will decide how spammy they are, and then I'll&lt;br/&gt;
post them. (Hopefully by typing 'rake comment --FILE=some_file' or something.)  There will need to be some initial filters in place, of course.  Anyway, we'll see.&lt;/p&gt;
&lt;h3&gt;So...&lt;/h3&gt;
&lt;p&gt;I'm looking at Rassmalog and Webby, mostly.  I think I might end up going with&lt;br/&gt;
Webby, and gluing some sort of hacked together comment script on to the bottom&lt;br/&gt;
of the thing.  It should last for a while.  Maybe a PHP script.  Or, a Ruby&lt;br/&gt;
script maybe?  Will I need a little Rack-based app?&lt;/p&gt;
&lt;p&gt;Is this what you would do?  Are you perfectly happy with WordPress, and you&lt;br/&gt;
think I'm insane (unsurprised, on both counts)?  Let me know.  Comment.&lt;br/&gt;
Please?&lt;/p&gt;
</content>
  </entry>
  
</feed>
