<?xml version="1.0" encoding="UTF-8"?>
<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
  <title>work.One Man's Walk - home</title>
  <id>tag:work.onemanswalk.com,2008:mephisto/</id>
  <generator uri="http://mephistoblog.com" version="0.7.3">Mephisto Noh-Varr</generator>
  <link href="http://work.onemanswalk.com/feed/rss.xml" rel="self" type="application/atom+xml"/>
  <link href="http://work.onemanswalk.com/" rel="alternate" type="text/html"/>
  <updated>2008-04-30T18:30:24Z</updated>
  <entry xml:base="http://work.onemanswalk.com/">
    <author>
      <name>jeremy</name>
    </author>
    <id>tag:work.onemanswalk.com,2008-04-30:1510</id>
    <published>2008-04-30T18:29:00Z</published>
    <updated>2008-04-30T18:30:24Z</updated>
    <link href="http://work.onemanswalk.com/2008/4/30/clickable-stack-traces-on-your-rails-error-page" rel="alternate" type="text/html"/>
    <title>Clickable Stack Traces on your Rails Error Page</title>
<content type="html">
            &lt;p&gt;Wouldn&#8217;t it be nice if when an error happened in your application, you could not only see the stack trace, but click on a line and jump to the offending code?  This is not groundbreaking stuff, I know, I had this like 10 years ago in C++ and later Java fat clients, and I&#8217;m sure other languages &#38; IDEs had it too &#8211; but somehow in moving to writing web apps in Ruby, I lost it.&lt;/p&gt;


	&lt;p&gt;I want it back damnit!&lt;/p&gt;


	&lt;p&gt;Turns out it&#8217;s pretty easy to get back (at least it is if you use textmate) &#8211; check it out.&lt;/p&gt;


	&lt;h2&gt;Custom Error Page&lt;/h2&gt;


	&lt;p&gt;First, to get a custom error page for your project, add something like this to your application.rb :&lt;/p&gt;


&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;r&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;fu&quot;&gt;rescue_action_locally&lt;/span&gt;(*args)&lt;tt&gt;
&lt;/tt&gt;  render &lt;span class=&quot;sy&quot;&gt;:template&lt;/span&gt;  =&amp;gt; &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;application/public_error&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;sy&quot;&gt;:layout&lt;/span&gt; =&amp;gt; &lt;span class=&quot;pc&quot;&gt;false&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;alias&lt;/span&gt; rescue_action_in_public render_action_locally&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;Note, if you&#8217;re using exception notifiable, you probably want to change the last line to something like :&lt;/p&gt;


&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;r&quot;&gt;alias&lt;/span&gt; render_404 rescue_action_locally&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;alias&lt;/span&gt; render_500 rescue_action_locally&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;We use markaby, so our public_error template looks something like this; it&#8217;s probably a good idea to keep this simple and not use a layout, just in case the error came from the layout :&lt;/p&gt;


&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;12&lt;tt&gt;
&lt;/tt&gt;13&lt;tt&gt;
&lt;/tt&gt;14&lt;tt&gt;
&lt;/tt&gt;15&lt;tt&gt;
&lt;/tt&gt;16&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;html &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  head &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    title action_name&lt;tt&gt;
&lt;/tt&gt;    stylesheet_link_tag &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  body &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    div.error &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      div.message &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;        h1 &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Whoops&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;        p &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;We detected an error.  Don't worry, though, &lt;tt&gt;
&lt;/tt&gt;we've been notified and we're on it.&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;h2&gt;Adding a Stack Trace w/ Links to the Error Page&lt;/h2&gt;


	&lt;p&gt;So, it would be helpful to us for our error page to tell us more in our development and staging environments.  We do use exception notifiable, so we don&#8217;t actually need or want it to say anything else to a real user in production.  Adding this to our template, it now looks like this :&lt;/p&gt;


&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;12&lt;tt&gt;
&lt;/tt&gt;13&lt;tt&gt;
&lt;/tt&gt;14&lt;tt&gt;
&lt;/tt&gt;15&lt;tt&gt;
&lt;/tt&gt;16&lt;tt&gt;
&lt;/tt&gt;17&lt;tt&gt;
&lt;/tt&gt;18&lt;tt&gt;
&lt;/tt&gt;19&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;21&lt;tt&gt;
&lt;/tt&gt;22&lt;tt&gt;
&lt;/tt&gt;23&lt;tt&gt;
&lt;/tt&gt;24&lt;tt&gt;
&lt;/tt&gt;25&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;html &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  head &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    title action_name&lt;tt&gt;
&lt;/tt&gt;    stylesheet_link_tag &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  body &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    div.error &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      div.message &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;        h1 &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Whoops&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;        &lt;tt&gt;
&lt;/tt&gt;        p &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;We detected an error.  Don't worry, though, &lt;tt&gt;
&lt;/tt&gt;we've been notified and we're on it.&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class=&quot;r&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;co&quot;&gt;RAILS_ENV&lt;/span&gt; != &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;production&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;        div.stack_trace &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;          h2 &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Stack Trace&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;          div { link_to_code &lt;span class=&quot;gv&quot;&gt;$!&lt;/span&gt;.to_s.to_s.gsub(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;&amp;lt;br/&amp;gt;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;) }&lt;tt&gt;
&lt;/tt&gt;          hr&lt;tt&gt;
&lt;/tt&gt;          div { link_to_code &lt;span class=&quot;gv&quot;&gt;$!&lt;/span&gt;.backtrace.join(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;&amp;lt;br/&amp;gt;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;) }&lt;tt&gt;
&lt;/tt&gt;        &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;What&#8217;s that &#8220;link_to_code&#8221; method in there?&lt;/p&gt;


	&lt;p&gt;It&#8217;s a method in application_helper that replaces any path with a textmate url to open up that file on your local system and jump to the offending line.  Check it out :&lt;/p&gt;


&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;r&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;fu&quot;&gt;link_to_code&lt;/span&gt;(text)&lt;tt&gt;
&lt;/tt&gt;  text.gsub(&lt;span class=&quot;rx&quot;&gt;&lt;span class=&quot;dl&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;\w&lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;\.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;-]*&lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;\/&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;\w&lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;\/&lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;\.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;-]+)&lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;\:&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;\d&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;+)&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;/&lt;/span&gt;&lt;/span&gt;) &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt; |match|&lt;tt&gt;
&lt;/tt&gt;    file = &lt;span class=&quot;gv&quot;&gt;$1&lt;/span&gt;.starts_with?(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;) ? &lt;span class=&quot;gv&quot;&gt;$1&lt;/span&gt; : &lt;span class=&quot;co&quot;&gt;File&lt;/span&gt;.join(&lt;span class=&quot;co&quot;&gt;RAILS_ROOT&lt;/span&gt;, &lt;span class=&quot;gv&quot;&gt;$1&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;    link_to match, &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;txmt://open?url=file://&lt;/span&gt;&lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;dl&quot;&gt;#{&lt;/span&gt;file&lt;span class=&quot;dl&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;&amp;amp;line=&lt;/span&gt;&lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;dl&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;gv&quot;&gt;$2&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;That&#8217;s it.  Suddenly, stack traces are friendly again!&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://work.onemanswalk.com/">
    <author>
      <name>jeremy</name>
    </author>
    <id>tag:work.onemanswalk.com,2008-04-23:1507</id>
    <published>2008-04-23T16:12:00Z</published>
    <updated>2008-04-23T16:13:27Z</updated>
    <link href="http://work.onemanswalk.com/2008/4/23/cruise-control-rb-is-moving-to-lighthouse" rel="alternate" type="text/html"/>
    <title>Cruise Control.rb is moving to Lighthouse</title>
<content type="html">
            &lt;p&gt;So, following rSpec&#8217;s example, we&#8217;re moving from &lt;span class=&quot;caps&quot;&gt;JIRA&lt;/span&gt; to Lighthouse.  Check us out at &lt;a href=&quot;http://cruisecontrolrb.lighthouseapp.com/&quot;&gt;http://cruisecontrolrb.lighthouseapp.com/&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;I have to say I wasn&#8217;t thrilled about Jira.  It does everything, and it does it slowly, with a UI that is bloated at best.  Lighthouse, on the other hand, keeps it &lt;span class=&quot;caps&quot;&gt;REALLY&lt;/span&gt; simple.  And does not a lot more than what we need.  It also just opened itself up for &lt;span class=&quot;caps&quot;&gt;OSS&lt;/span&gt; projects (which is cool).&lt;/p&gt;


	&lt;p&gt;How did we migrate all of our stories from &lt;span class=&quot;caps&quot;&gt;JIRA&lt;/span&gt; to Lighthouse?  I thought you&#8217;d never ask.&lt;/p&gt;


	&lt;p&gt;Lighthouse exposes a slick RESTful &lt;span class=&quot;caps&quot;&gt;API&lt;/span&gt; for managing your data.  I exported the &lt;span class=&quot;caps&quot;&gt;JIRA&lt;/span&gt; issues into a csv file, wrote a short ruby script, waited about 5 minutes for it to run, and I was done.&lt;/p&gt;


	&lt;p&gt;Here&#8217;s the script:&lt;/p&gt;


&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;12&lt;tt&gt;
&lt;/tt&gt;13&lt;tt&gt;
&lt;/tt&gt;14&lt;tt&gt;
&lt;/tt&gt;15&lt;tt&gt;
&lt;/tt&gt;16&lt;tt&gt;
&lt;/tt&gt;17&lt;tt&gt;
&lt;/tt&gt;18&lt;tt&gt;
&lt;/tt&gt;19&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;21&lt;tt&gt;
&lt;/tt&gt;22&lt;tt&gt;
&lt;/tt&gt;23&lt;tt&gt;
&lt;/tt&gt;24&lt;tt&gt;
&lt;/tt&gt;25&lt;tt&gt;
&lt;/tt&gt;26&lt;tt&gt;
&lt;/tt&gt;27&lt;tt&gt;
&lt;/tt&gt;28&lt;tt&gt;
&lt;/tt&gt;29&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;30&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;31&lt;tt&gt;
&lt;/tt&gt;32&lt;tt&gt;
&lt;/tt&gt;33&lt;tt&gt;
&lt;/tt&gt;34&lt;tt&gt;
&lt;/tt&gt;35&lt;tt&gt;
&lt;/tt&gt;36&lt;tt&gt;
&lt;/tt&gt;37&lt;tt&gt;
&lt;/tt&gt;38&lt;tt&gt;
&lt;/tt&gt;39&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;40&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;41&lt;tt&gt;
&lt;/tt&gt;42&lt;tt&gt;
&lt;/tt&gt;43&lt;tt&gt;
&lt;/tt&gt;44&lt;tt&gt;
&lt;/tt&gt;45&lt;tt&gt;
&lt;/tt&gt;46&lt;tt&gt;
&lt;/tt&gt;47&lt;tt&gt;
&lt;/tt&gt;48&lt;tt&gt;
&lt;/tt&gt;49&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;50&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;51&lt;tt&gt;
&lt;/tt&gt;52&lt;tt&gt;
&lt;/tt&gt;53&lt;tt&gt;
&lt;/tt&gt;54&lt;tt&gt;
&lt;/tt&gt;55&lt;tt&gt;
&lt;/tt&gt;56&lt;tt&gt;
&lt;/tt&gt;57&lt;tt&gt;
&lt;/tt&gt;58&lt;tt&gt;
&lt;/tt&gt;59&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;60&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;61&lt;tt&gt;
&lt;/tt&gt;62&lt;tt&gt;
&lt;/tt&gt;63&lt;tt&gt;
&lt;/tt&gt;64&lt;tt&gt;
&lt;/tt&gt;65&lt;tt&gt;
&lt;/tt&gt;66&lt;tt&gt;
&lt;/tt&gt;67&lt;tt&gt;
&lt;/tt&gt;68&lt;tt&gt;
&lt;/tt&gt;69&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;70&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;71&lt;tt&gt;
&lt;/tt&gt;72&lt;tt&gt;
&lt;/tt&gt;73&lt;tt&gt;
&lt;/tt&gt;74&lt;tt&gt;
&lt;/tt&gt;75&lt;tt&gt;
&lt;/tt&gt;76&lt;tt&gt;
&lt;/tt&gt;77&lt;tt&gt;
&lt;/tt&gt;78&lt;tt&gt;
&lt;/tt&gt;79&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;80&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;81&lt;tt&gt;
&lt;/tt&gt;82&lt;tt&gt;
&lt;/tt&gt;83&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;#!/usr/bin/env ruby&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;require &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;rubygems&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;require &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;csv&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;co&quot;&gt;JiraIssue&lt;/span&gt; = &lt;span class=&quot;co&quot;&gt;Struct&lt;/span&gt;.new(&lt;span class=&quot;sy&quot;&gt;:issue_type&lt;/span&gt;, &lt;span class=&quot;sy&quot;&gt;:key&lt;/span&gt;, &lt;span class=&quot;sy&quot;&gt;:status&lt;/span&gt;, &lt;span class=&quot;sy&quot;&gt;:assign_to&lt;/span&gt;, &lt;tt&gt;
&lt;/tt&gt;                       &lt;span class=&quot;sy&quot;&gt;:summary&lt;/span&gt;, &lt;span class=&quot;sy&quot;&gt;:description&lt;/span&gt;, &lt;span class=&quot;sy&quot;&gt;:fix_version&lt;/span&gt;, &lt;tt&gt;
&lt;/tt&gt;                       &lt;span class=&quot;sy&quot;&gt;:reporter&lt;/span&gt;, &lt;span class=&quot;sy&quot;&gt;:priority&lt;/span&gt;, &lt;span class=&quot;sy&quot;&gt;:original_estimate&lt;/span&gt;, &lt;tt&gt;
&lt;/tt&gt;                       &lt;span class=&quot;sy&quot;&gt;:votes&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;fu&quot;&gt;load_issues&lt;/span&gt;(csv_file)&lt;tt&gt;
&lt;/tt&gt;  header = &lt;span class=&quot;pc&quot;&gt;true&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  issues = []&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;co&quot;&gt;CSV&lt;/span&gt;.open(csv_file, &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;) &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt; |row|&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;if&lt;/span&gt; header&lt;tt&gt;
&lt;/tt&gt;      header = &lt;span class=&quot;pc&quot;&gt;false&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;else&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      issues &amp;lt;&amp;lt; &lt;span class=&quot;co&quot;&gt;JiraIssue&lt;/span&gt;.new(*row)&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  issues&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;issues = load_issues(&lt;span class=&quot;co&quot;&gt;File&lt;/span&gt;.dirname(&lt;span class=&quot;pc&quot;&gt;__FILE__&lt;/span&gt;) + &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;/jiraissues.csv&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;# this code is just to examine what our JIRA data looks like&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;fu&quot;&gt;show&lt;/span&gt;(issues, field)&lt;tt&gt;
&lt;/tt&gt;  puts &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;dl&quot;&gt;#{&lt;/span&gt;field&lt;span class=&quot;dl&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt; = &lt;/span&gt;&lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;dl&quot;&gt;#{&lt;/span&gt;issues.map{|i| i.send(field)}.uniq.inspect&lt;span class=&quot;dl&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;show issues, &lt;span class=&quot;sy&quot;&gt;:issue_type&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;show issues, &lt;span class=&quot;sy&quot;&gt;:status&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;show issues, &lt;span class=&quot;sy&quot;&gt;:assign_to&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;show issues, &lt;span class=&quot;sy&quot;&gt;:fix_version&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;show issues, &lt;span class=&quot;sy&quot;&gt;:reporter&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;show issues, &lt;span class=&quot;sy&quot;&gt;:priority&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;show issues, &lt;span class=&quot;sy&quot;&gt;:original_estimate&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;show issues, &lt;span class=&quot;sy&quot;&gt;:votes&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;# these hashes translate between JIRA and Lighthouse&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;states = {&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Open&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;,&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Resolved&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;resolved&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;,&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Closed&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;}&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;users = {&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Unassigned&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class=&quot;pc&quot;&gt;nil&lt;/span&gt;,&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Alexey Verkhovsky&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class=&quot;i&quot;&gt;10545&lt;/span&gt;, &lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Rolf Russell&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class=&quot;i&quot;&gt;17992&lt;/span&gt;, &lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Jeremy Stell-Smith&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class=&quot;i&quot;&gt;10638&lt;/span&gt;, &lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Zhang Lin&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class=&quot;pc&quot;&gt;nil&lt;/span&gt;, &lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Jeff Xiong&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class=&quot;pc&quot;&gt;nil&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;}&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;milestones = {&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class=&quot;i&quot;&gt;9885&lt;/span&gt;,&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;1.1&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class=&quot;i&quot;&gt;9886&lt;/span&gt;, &lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;1.2&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class=&quot;i&quot;&gt;9887&lt;/span&gt;, &lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;1.3&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class=&quot;i&quot;&gt;9888&lt;/span&gt;, &lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;1.4&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class=&quot;i&quot;&gt;9889&lt;/span&gt;, &lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;\302&lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;\240&lt;/span&gt;&lt;span class=&quot;k&quot;&gt; &lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class=&quot;pc&quot;&gt;nil&lt;/span&gt;, &lt;tt&gt;
&lt;/tt&gt;}&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;require &lt;span class=&quot;co&quot;&gt;File&lt;/span&gt;.dirname(&lt;span class=&quot;pc&quot;&gt;__FILE__&lt;/span&gt;) + &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;/lib/lighthouse&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;include &lt;span class=&quot;co&quot;&gt;Lighthouse&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;co&quot;&gt;Lighthouse&lt;/span&gt;.account = &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;cruisecontrolrb&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;# enter your own user, password here...&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;co&quot;&gt;Lighthouse&lt;/span&gt;.authenticate &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;jeremystellsmith@gmail.com&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;*******&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;issues.each &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt; |i|&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;c&quot;&gt;# enter your own project id here...&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  ticket = &lt;span class=&quot;co&quot;&gt;Ticket&lt;/span&gt;.new &lt;span class=&quot;sy&quot;&gt;:project_id&lt;/span&gt; =&amp;gt; &lt;span class=&quot;i&quot;&gt;9150&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  ticket.title = i.summary&lt;tt&gt;
&lt;/tt&gt;  ticket.body = i.description&lt;tt&gt;
&lt;/tt&gt;  ticket.state = states[i.status.to_s.strip]&lt;tt&gt;
&lt;/tt&gt;  ticket.assigned_user_id = users[i.assign_to.to_s.strip]&lt;tt&gt;
&lt;/tt&gt;  ticket.milestone_id = milestones[i.fix_version]&lt;tt&gt;
&lt;/tt&gt;  ticket.tags &amp;lt;&amp;lt; i.issue_type &amp;lt;&amp;lt; i.priority&lt;tt&gt;
&lt;/tt&gt;  &lt;tt&gt;
&lt;/tt&gt;  ticket.save&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;There was another step.  I ran the first part of the script a couple times to tell me what the different issue_types, statuses, assigned users, fix versions, etc were that my &lt;span class=&quot;caps&quot;&gt;JIRA&lt;/span&gt; data was using.  I then found or created the appropriate data in Lighthouse and added some hashes to translate for me.&lt;/p&gt;


	&lt;p&gt;All in all, I&#8217;m pretty thrilled with how easy this was.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://work.onemanswalk.com/">
    <author>
      <name>jeremy</name>
    </author>
    <id>tag:work.onemanswalk.com,2007-12-13:1106</id>
    <published>2007-12-13T19:07:00Z</published>
    <updated>2007-12-13T19:15:36Z</updated>
    <link href="http://work.onemanswalk.com/2007/12/13/using-markaby-w-rails-2-0-1" rel="alternate" type="text/html"/>
    <title>Using Markaby w/ Rails 2.0.1</title>
<content type="html">
            &lt;p&gt;Rails 2.0.1 is out, in general, it&#8217;s pretty nice.  Markaby doesn&#8217;t play too nice w/ it.  Markaby requires every named route to have a .to_s on the end of it.  Yuck.&lt;/p&gt;


	&lt;p&gt;After migrating my 3rd project to rails 2.0.1, I got sick of adding .to_s to each named routes, and did some digging.&lt;/p&gt;


	&lt;p&gt;Apparently, somehow a string coming from a named route is not &lt;em&gt;exactly&lt;/em&gt; a string.  Sure&lt;/p&gt;


&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;  apples_path().class == &lt;span class=&quot;co&quot;&gt;String&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;but&lt;/p&gt;


&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;co&quot;&gt;String&lt;/span&gt; === apples_path()&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;is &lt;span class=&quot;caps&quot;&gt;NOT&lt;/span&gt; true.  I couldn&#8217;t quite track down why this was but it eventually leads to a&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;ActionView::TemplateError (undefined method `string_path' for #) on line #10 of messages/index.mab:

actionpack-2.0.1/lib/action_controller/polymorphic_routes.rb:27:in `send!'
actionpack-2.0.1/lib/action_controller/polymorphic_routes.rb:27:in `polymorphic_url'
actionpack-2.0.1/lib/action_controller/polymorphic_routes.rb:31:in `polymorphic_path'
actionpack-2.0.1/lib/action_view/helpers/url_helper.rb:79:in `url_for'
actionpack-2.0.1/lib/action_view/helpers/url_helper.rb:144:in `link_to'
...&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;After an hour of trying in vain to find the source of the problem, I found a simple hack that fixes this.  Should have thought of this before, but add this code to your application_helper.rb&lt;/p&gt;


&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;r&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;fu&quot;&gt;string_path&lt;/span&gt;(string)&lt;tt&gt;
&lt;/tt&gt;    string&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;If anyone else has a better solution, please pass it over!&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://work.onemanswalk.com/">
    <author>
      <name>jeremy</name>
    </author>
    <id>tag:work.onemanswalk.com,2007-11-04:1001</id>
    <published>2007-11-04T22:56:00Z</published>
    <updated>2007-11-04T23:11:07Z</updated>
    <link href="http://work.onemanswalk.com/2007/11/4/working-with-iwork-and-subversion" rel="alternate" type="text/html"/>
    <title>Working with iWork and Subversion</title>
<content type="html">
            &lt;p&gt;So I&#8217;m a big fan of iWork &#8216;08, pages is nice enough, doesn&#8217;t get in my way, etc.  Numbers is pretty awesome.  It&#8217;s been a while since someone did something revolutionary in the spreadsheet world.  Numbers is it.&lt;/p&gt;


	&lt;p&gt;I do have a couple gripes.&lt;/p&gt;


	&lt;p&gt;1) if I open a word or excel file, when I save, it should be saved back to that same word or excel file.  Not all my friends have macs, let alone iWork, why can&#8217;t I save back to the file I opened?  And if I have to do the extra export step, why does iWork forget where the file came from?  Grr.&lt;/p&gt;


	&lt;p&gt;2) I am a total excel power user, and numbers is still missing a bunch of features for addins, macros, etc.  It doesn&#8217;t even have applescript support yet as far as I can tell (I&#8217;m sure it will, though, and I am glad they released before they had it)  I&#8217;ve been getting by on a manual export to csv so I can munge data.&lt;/p&gt;


	&lt;p&gt;3) and what this blog post is about, is that iWork does not play nice w/ subversion.&lt;/p&gt;


	&lt;p&gt;iWork stores it&#8217;s files in &#8220;bundles&#8221; which are actually directories even though they look like files from finder.  When you have stored one of these bundles in subversion, there will be .svn directories inside of it, containing subversion metadata.  The problem comes that every time you save a file in iWork, the entire bundle is replaced w/ the new copy&#8230;and &lt;span class=&quot;caps&quot;&gt;ALL&lt;/span&gt; .svn directories are destroyed.&lt;/p&gt;


	&lt;p&gt;This basically renders subversion useless for iWork files.  This is no good, there are some pretty painful &lt;a href=&quot;http://www.jpbutler.com/2007/08/29/workaround-for-iwork-and-subversion/&quot;&gt;workarounds&lt;/a&gt; on the net, and there are a few scripts that turned up, but I wrote my own.&lt;/p&gt;


	&lt;p&gt;This script (I call it svn_restore_dirs) will search any subdirectories of the current working directory for files ending in .numbers, .pages, etc.  It will then redownload .svn directories for any of these that are missing theirs.&lt;/p&gt;


	&lt;p&gt;Seems to work, and is simple enough.  I wish apple would get their act together.&lt;/p&gt;


	&lt;p&gt;Enjoy the script&lt;/p&gt;


&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;12&lt;tt&gt;
&lt;/tt&gt;13&lt;tt&gt;
&lt;/tt&gt;14&lt;tt&gt;
&lt;/tt&gt;15&lt;tt&gt;
&lt;/tt&gt;16&lt;tt&gt;
&lt;/tt&gt;17&lt;tt&gt;
&lt;/tt&gt;18&lt;tt&gt;
&lt;/tt&gt;19&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;21&lt;tt&gt;
&lt;/tt&gt;22&lt;tt&gt;
&lt;/tt&gt;23&lt;tt&gt;
&lt;/tt&gt;24&lt;tt&gt;
&lt;/tt&gt;25&lt;tt&gt;
&lt;/tt&gt;26&lt;tt&gt;
&lt;/tt&gt;27&lt;tt&gt;
&lt;/tt&gt;28&lt;tt&gt;
&lt;/tt&gt;29&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;30&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;31&lt;tt&gt;
&lt;/tt&gt;32&lt;tt&gt;
&lt;/tt&gt;33&lt;tt&gt;
&lt;/tt&gt;34&lt;tt&gt;
&lt;/tt&gt;35&lt;tt&gt;
&lt;/tt&gt;36&lt;tt&gt;
&lt;/tt&gt;37&lt;tt&gt;
&lt;/tt&gt;38&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;#!/usr/bin/env ruby&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;require &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fileutils&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;co&quot;&gt;EXTENSIONS_TO_SEARCH&lt;/span&gt; = &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;%w(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;numbers pages key graffle&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;co&quot;&gt;TMP_DIR&lt;/span&gt; = &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;/tmp/missing_svn&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;fu&quot;&gt;find_directories_without_svn_dirs&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  search_pattern = &lt;span class=&quot;co&quot;&gt;EXTENSIONS_TO_SEARCH&lt;/span&gt;.map {|ext| &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;-name &lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;*.&lt;/span&gt;&lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;dl&quot;&gt;#{&lt;/span&gt;ext&lt;span class=&quot;dl&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;}.join(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt; -or &lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;  dirs = &lt;span class=&quot;sh&quot;&gt;&lt;span class=&quot;dl&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;find . &lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;( &lt;/span&gt;&lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;dl&quot;&gt;#{&lt;/span&gt;search_pattern&lt;span class=&quot;dl&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;) -type d&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;`&lt;/span&gt;&lt;/span&gt;.split(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;  dirs.reject {|dir| &lt;span class=&quot;co&quot;&gt;File&lt;/span&gt;.exists?(&lt;span class=&quot;co&quot;&gt;File&lt;/span&gt;.join(dir, &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;.svn&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;))}&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;  &lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;fu&quot;&gt;get_url_for&lt;/span&gt;(file)&lt;tt&gt;
&lt;/tt&gt;  info = &lt;span class=&quot;sh&quot;&gt;&lt;span class=&quot;dl&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;svn info &amp;quot;&lt;/span&gt;&lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;dl&quot;&gt;#{&lt;/span&gt;file&lt;span class=&quot;dl&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  raise &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;can't parse &lt;/span&gt;&lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;dl&quot;&gt;#{&lt;/span&gt;info&lt;span class=&quot;dl&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;r&quot;&gt;unless&lt;/span&gt; info =~ &lt;span class=&quot;rx&quot;&gt;&lt;span class=&quot;dl&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;\:&lt;/span&gt;&lt;span class=&quot;k&quot;&gt; (.*)&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;gv&quot;&gt;$1&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;fu&quot;&gt;copy_svn_dirs&lt;/span&gt;(from, to)&lt;tt&gt;
&lt;/tt&gt;  to = &lt;span class=&quot;co&quot;&gt;File&lt;/span&gt;.expand_path(to)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;co&quot;&gt;Dir&lt;/span&gt;.chdir from &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;co&quot;&gt;Dir&lt;/span&gt;[&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;**/.svn&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;].each &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt; |svn_dir|&lt;tt&gt;
&lt;/tt&gt;      &lt;span class=&quot;co&quot;&gt;FileUtils&lt;/span&gt;.mv svn_dir, &lt;span class=&quot;co&quot;&gt;File&lt;/span&gt;.join(to, svn_dir)&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;find_directories_without_svn_dirs.each &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt; |dir|&lt;tt&gt;
&lt;/tt&gt;  url = get_url_for(&lt;span class=&quot;co&quot;&gt;File&lt;/span&gt;.dirname(dir))&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  puts &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;replacing svn dirs for &lt;/span&gt;&lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;dl&quot;&gt;#{&lt;/span&gt;dir&lt;span class=&quot;dl&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt; from &lt;/span&gt;&lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;dl&quot;&gt;#{&lt;/span&gt;url&lt;span class=&quot;dl&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;dl&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;co&quot;&gt;File&lt;/span&gt;.basename(dir)&lt;span class=&quot;dl&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;co&quot;&gt;FileUtils&lt;/span&gt;.rm_rf(&lt;span class=&quot;co&quot;&gt;TMP_DIR&lt;/span&gt;) &lt;span class=&quot;r&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;co&quot;&gt;File&lt;/span&gt;.exists?(&lt;span class=&quot;co&quot;&gt;TMP_DIR&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;sh&quot;&gt;&lt;span class=&quot;dl&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;svn co &amp;quot;&lt;/span&gt;&lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;dl&quot;&gt;#{&lt;/span&gt;url&lt;span class=&quot;dl&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;dl&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;co&quot;&gt;File&lt;/span&gt;.basename(dir)&lt;span class=&quot;dl&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;&amp;quot; &lt;/span&gt;&lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;dl&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;co&quot;&gt;TMP_DIR&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  copy_svn_dirs(&lt;span class=&quot;co&quot;&gt;TMP_DIR&lt;/span&gt;, dir) &lt;span class=&quot;r&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;gv&quot;&gt;$?&lt;/span&gt; == &lt;span class=&quot;i&quot;&gt;0&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;
          </content>  </entry>
  <entry xml:base="http://work.onemanswalk.com/">
    <author>
      <name>jeremy</name>
    </author>
    <id>tag:work.onemanswalk.com,2007-10-16:897</id>
    <published>2007-10-16T05:28:00Z</published>
    <updated>2007-10-16T05:35:38Z</updated>
    <link href="http://work.onemanswalk.com/2007/10/16/pretty-printing-seconds-in-ruby" rel="alternate" type="text/html"/>
    <title>Pretty Printing Seconds in Ruby</title>
<content type="html">
            &lt;p&gt;I must have written various versions of this code 20 times so far in my career, but never so quickly and cleanly.  I love ruby.&lt;/p&gt;


&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;12&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;    min, sec = sec / &lt;span class=&quot;i&quot;&gt;60&lt;/span&gt;, sec % &lt;span class=&quot;i&quot;&gt;60&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    hour, min = min / &lt;span class=&quot;i&quot;&gt;60&lt;/span&gt;, min % &lt;span class=&quot;i&quot;&gt;60&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    day, hour = hour / &lt;span class=&quot;i&quot;&gt;24&lt;/span&gt;, hour % &lt;span class=&quot;i&quot;&gt;24&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    week, day = day / &lt;span class=&quot;i&quot;&gt;7&lt;/span&gt;, day % &lt;span class=&quot;i&quot;&gt;7&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;tt&gt;
&lt;/tt&gt;    [&lt;tt&gt;
&lt;/tt&gt;      week &amp;gt; &lt;span class=&quot;i&quot;&gt;0&lt;/span&gt; ? &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;dl&quot;&gt;#{&lt;/span&gt;week&lt;span class=&quot;dl&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt; weeks&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; : &lt;span class=&quot;pc&quot;&gt;nil&lt;/span&gt;,&lt;tt&gt;
&lt;/tt&gt;      day &amp;gt; &lt;span class=&quot;i&quot;&gt;0&lt;/span&gt; ? &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;dl&quot;&gt;#{&lt;/span&gt;day&lt;span class=&quot;dl&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt; days&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; : &lt;span class=&quot;pc&quot;&gt;nil&lt;/span&gt;,&lt;tt&gt;
&lt;/tt&gt;      hour &amp;gt; &lt;span class=&quot;i&quot;&gt;0&lt;/span&gt; ? &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;dl&quot;&gt;#{&lt;/span&gt;hour&lt;span class=&quot;dl&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt; hours&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; : &lt;span class=&quot;pc&quot;&gt;nil&lt;/span&gt;,&lt;tt&gt;
&lt;/tt&gt;      min &amp;gt; &lt;span class=&quot;i&quot;&gt;0&lt;/span&gt; ? &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;dl&quot;&gt;#{&lt;/span&gt;min&lt;span class=&quot;dl&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt; minutes&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; : &lt;span class=&quot;pc&quot;&gt;nil&lt;/span&gt;,&lt;tt&gt;
&lt;/tt&gt;      sec &amp;gt; &lt;span class=&quot;i&quot;&gt;0&lt;/span&gt; ? &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;dl&quot;&gt;#{&lt;/span&gt;sec&lt;span class=&quot;dl&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt; seconds&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; : &lt;span class=&quot;pc&quot;&gt;nil&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    ].compact.join(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;The really cool thing is the multiple assignment that ruby lets you do.  I haven&#8217;t used it for something exactly like this before, but I do use it often and it&#8217;s really nice.&lt;/p&gt;


	&lt;p&gt;I feel like there should be a better way to do the bottom half of this, but this is pretty damn readable, I think.  You might have to have a little rubyFU to remember what compact does &#8211; which is get rid of the nils.&lt;/p&gt;


	&lt;p&gt;Anyway, this was about 15 minutes of work (and I did it test first).&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://work.onemanswalk.com/">
    <author>
      <name>jeremy</name>
    </author>
    <id>tag:work.onemanswalk.com,2007-09-26:826</id>
    <published>2007-09-26T23:14:00Z</published>
    <updated>2007-09-26T23:15:23Z</updated>
    <link href="http://work.onemanswalk.com/2007/9/26/customizing-markaby-language-level-refactorings" rel="alternate" type="text/html"/>
    <title>Customizing Markaby - Language Level Refactorings</title>
<content type="html">
            &lt;p&gt;It&#8217;s very easy to call out to methods in markaby, but it&#8217;d be nice if you could actually customize the dsl as well.&lt;/p&gt;


	&lt;p&gt;For example, on many of our pages we have a bottom row that has buttons that look a certain way.  So on every page, we have :&lt;/p&gt;


&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;12&lt;tt&gt;
&lt;/tt&gt;13&lt;tt&gt;
&lt;/tt&gt;14&lt;tt&gt;
&lt;/tt&gt;15&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;  table(&lt;span class=&quot;sy&quot;&gt;:width&lt;/span&gt; =&amp;gt; &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;100%&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;) &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    tr &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      td.left &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;        previous_button&lt;tt&gt;
&lt;/tt&gt;        first_button&lt;tt&gt;
&lt;/tt&gt;      &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      td.center &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;        print&lt;tt&gt;
&lt;/tt&gt;      &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      td.right &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;        next_button&lt;tt&gt;
&lt;/tt&gt;        last_button&lt;tt&gt;
&lt;/tt&gt;      &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;The code for the actual buttons changes, and after a few tries to extract the whole thing into a single method, we gave up.  Our efforts had made it harder to read, not easier.  There was always just a little too much variance, and it didn&#8217;t feel right.&lt;/p&gt;


	&lt;p&gt;What we really wanted to write was :&lt;/p&gt;


&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;12&lt;tt&gt;
&lt;/tt&gt;13&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;  last_row &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    column &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      previous_button&lt;tt&gt;
&lt;/tt&gt;      first_button&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    column &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      print&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    column &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      next_button&lt;tt&gt;
&lt;/tt&gt;      last_button&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;This lets the buttons that change all stay in the view, and gets rid of the skeleton and positional stuff that doesn&#8217;t change.  Furthermore, it&#8217;s &lt;span class=&quot;caps&quot;&gt;DRY&lt;/span&gt; and puts all that positional logic in one place instead of scattered across 20 views.&lt;/p&gt;


	&lt;p&gt;How to do this?&lt;/p&gt;


	&lt;p&gt;I wrote a test (in RSpec) that looks something like :&lt;/p&gt;


&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;12&lt;tt&gt;
&lt;/tt&gt;13&lt;tt&gt;
&lt;/tt&gt;14&lt;tt&gt;
&lt;/tt&gt;15&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;describe &lt;span class=&quot;co&quot;&gt;ApplicationHelper&lt;/span&gt; &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  it &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;should generate a table from a buttons method&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    last_row(&lt;span class=&quot;sy&quot;&gt;:columns&lt;/span&gt; =&amp;gt; &lt;span class=&quot;i&quot;&gt;2&lt;/span&gt;) &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      column &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;        &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      column &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;        &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;.should == &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;&amp;lt;table width=&amp;quot;100%&amp;quot;&amp;gt;&amp;lt;tr&amp;gt;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt; +&lt;tt&gt;
&lt;/tt&gt;                    &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;&amp;lt;td class=&amp;quot;left&amp;quot;&amp;gt;foo&amp;lt;/td&amp;gt;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt; + &lt;tt&gt;
&lt;/tt&gt;                    &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;&amp;lt;td class=&amp;quot;right&amp;quot;&amp;gt;bar&amp;lt;/td&amp;gt;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt; +&lt;tt&gt;
&lt;/tt&gt;                  &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;After a bunch of fiddling and poking around, I finally made the test (and a couple others) pass with this code in my ApplicationHelper :&lt;/p&gt;


&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;12&lt;tt&gt;
&lt;/tt&gt;13&lt;tt&gt;
&lt;/tt&gt;14&lt;tt&gt;
&lt;/tt&gt;15&lt;tt&gt;
&lt;/tt&gt;16&lt;tt&gt;
&lt;/tt&gt;17&lt;tt&gt;
&lt;/tt&gt;18&lt;tt&gt;
&lt;/tt&gt;19&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;21&lt;tt&gt;
&lt;/tt&gt;22&lt;tt&gt;
&lt;/tt&gt;23&lt;tt&gt;
&lt;/tt&gt;24&lt;tt&gt;
&lt;/tt&gt;25&lt;tt&gt;
&lt;/tt&gt;26&lt;tt&gt;
&lt;/tt&gt;27&lt;tt&gt;
&lt;/tt&gt;28&lt;tt&gt;
&lt;/tt&gt;29&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;r&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;fu&quot;&gt;last_row&lt;/span&gt;(options, &amp;amp;block)&lt;tt&gt;
&lt;/tt&gt;  markaby &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    table(&lt;span class=&quot;sy&quot;&gt;:width&lt;/span&gt; =&amp;gt; &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;100%&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;) &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      tr &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;        &lt;span class=&quot;co&quot;&gt;LastRowContext&lt;/span&gt;.new(&lt;span class=&quot;pc&quot;&gt;self&lt;/span&gt;, options[&lt;span class=&quot;sy&quot;&gt;:columns&lt;/span&gt;]).&lt;tt&gt;
&lt;/tt&gt;                                  instance_eval(&amp;amp;block)&lt;tt&gt;
&lt;/tt&gt;      &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;cl&quot;&gt;LastRowContext&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;fu&quot;&gt;initialize&lt;/span&gt;(markaby, columns)&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;iv&quot;&gt;@markaby&lt;/span&gt;, &lt;span class=&quot;iv&quot;&gt;@column_count&lt;/span&gt;, &lt;span class=&quot;iv&quot;&gt;@column_index&lt;/span&gt; = &lt;tt&gt;
&lt;/tt&gt;                            markaby, columns, &lt;span class=&quot;i&quot;&gt;0&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;fu&quot;&gt;column&lt;/span&gt;(&amp;amp;block)&lt;tt&gt;
&lt;/tt&gt;    alignment = &lt;span class=&quot;r&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;iv&quot;&gt;@column_index&lt;/span&gt; += &lt;span class=&quot;i&quot;&gt;1&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;i&quot;&gt;1&lt;/span&gt; : &lt;span class=&quot;sy&quot;&gt;:left&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;iv&quot;&gt;@column_count&lt;/span&gt; : &lt;span class=&quot;sy&quot;&gt;:right&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;sy&quot;&gt;:center&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;iv&quot;&gt;@markaby&lt;/span&gt;.instance_eval &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      td(&lt;span class=&quot;sy&quot;&gt;:class&lt;/span&gt; =&amp;gt; alignment, &amp;amp;block)&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;I&#8217;m sure this could get cleaned up more; this was the work of less than an hour.  In particular, if you did this often, you could extract a common MarkabyContext superclass that had some convenience methods.  The point is, this is really easy to do, and we shouldn&#8217;t be scared to try &#8220;Language level refactorings&#8221; like this.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://work.onemanswalk.com/">
    <author>
      <name>jeremy</name>
    </author>
    <id>tag:work.onemanswalk.com,2007-09-20:811</id>
    <published>2007-09-20T18:28:00Z</published>
    <updated>2007-09-20T18:29:46Z</updated>
    <link href="http://work.onemanswalk.com/2007/9/20/daemonizing-a-ruby-script-in-rails" rel="alternate" type="text/html"/>
    <title>Daemonizing a Ruby Script in Rails</title>
<content type="html">
            &lt;p&gt;This took &lt;em&gt;way&lt;/em&gt; longer than it should have, so I thought I&#8217;d jot down what I did so it might take less time next time.&lt;/p&gt;


	&lt;p&gt;1. Install the &lt;a href=&quot;http://daemons.rubyforge.org&quot;&gt;daemons&lt;/a&gt; gem&lt;/p&gt;


&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;sudo gem install daemons&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;2. Presumably you have a script that looks something like this already&lt;/p&gt;


&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;#!/usr/bin/env ruby&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;require &lt;span class=&quot;co&quot;&gt;File&lt;/span&gt;.dirname(&lt;span class=&quot;pc&quot;&gt;__FILE__&lt;/span&gt;) + &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;/. ./config/environment&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;co&quot;&gt;SchedulerDaemon&lt;/span&gt;.new.run&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;This is a scheduler script that lives in the scripts directory of a rails project.&lt;/p&gt;


	&lt;p&gt;3. You&#8217;re going to take this code and wrap it in daemon stuff, like so&lt;/p&gt;


&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;12&lt;tt&gt;
&lt;/tt&gt;13&lt;tt&gt;
&lt;/tt&gt;14&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;#!/usr/bin/env ruby&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;co&quot;&gt;RAILS_ROOT&lt;/span&gt; = &lt;span class=&quot;co&quot;&gt;File&lt;/span&gt;.expand_path(&lt;span class=&quot;co&quot;&gt;File&lt;/span&gt;.dirname(&lt;span class=&quot;pc&quot;&gt;__FILE__&lt;/span&gt;) + &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;/. .&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;require &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;rubygems&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;gem &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;daemons&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;require &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;daemons&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;co&quot;&gt;Daemons&lt;/span&gt;.run_proc(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;scheduler&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;tt&gt;
&lt;/tt&gt;                 &lt;span class=&quot;sy&quot;&gt;:log_output&lt;/span&gt; =&amp;gt; &lt;span class=&quot;pc&quot;&gt;true&lt;/span&gt;, &lt;tt&gt;
&lt;/tt&gt;                 &lt;span class=&quot;sy&quot;&gt;:dir_mode&lt;/span&gt; =&amp;gt; &lt;span class=&quot;sy&quot;&gt;:normal&lt;/span&gt;, &lt;tt&gt;
&lt;/tt&gt;                 &lt;span class=&quot;sy&quot;&gt;:dir&lt;/span&gt; =&amp;gt; &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;dl&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;co&quot;&gt;RAILS_ROOT&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;/log&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;) &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  require &lt;span class=&quot;co&quot;&gt;File&lt;/span&gt;.join(&lt;span class=&quot;co&quot;&gt;RAILS_ROOT&lt;/span&gt;, &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;config/environment&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;co&quot;&gt;SchedulerDaemon&lt;/span&gt;.new.run&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;The really hard part for me was debugging it.  Which meant figuring out how to get logging going.  With this code, you can just tail &#8220;log/scheduler.output&#8221; and see the contents of any puts in the code.  Once I started doing that, everything else was easy.&lt;/p&gt;


	&lt;p&gt;&lt;span class=&quot;caps&quot;&gt;RAILS&lt;/span&gt;_ROOT has to be set first, because daemons changes the current working directory.  Also, I use the log dir, because it&#8217;s shared by capistrano, so I can deploy, then stop / start my scheduler and not worry about losing my first pid file &#8211; plus that&#8217;s where logs are supposed to go.&lt;/p&gt;


	&lt;p&gt;4. To make my life just a little easier, I also added some debugging statements&lt;/p&gt;


&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;12&lt;tt&gt;
&lt;/tt&gt;13&lt;tt&gt;
&lt;/tt&gt;14&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;co&quot;&gt;Daemons&lt;/span&gt;.run_proc(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;scheduler&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;tt&gt;
&lt;/tt&gt;                 &lt;span class=&quot;sy&quot;&gt;:log_output&lt;/span&gt; =&amp;gt; &lt;span class=&quot;pc&quot;&gt;true&lt;/span&gt;, &lt;tt&gt;
&lt;/tt&gt;                 &lt;span class=&quot;sy&quot;&gt;:dir_mode&lt;/span&gt; =&amp;gt; &lt;span class=&quot;sy&quot;&gt;:normal&lt;/span&gt;, &lt;tt&gt;
&lt;/tt&gt;                 &lt;span class=&quot;sy&quot;&gt;:dir&lt;/span&gt; =&amp;gt; &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;dl&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;co&quot;&gt;RAILS_ROOT&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;/log&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;) &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;begin&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    require &lt;span class=&quot;co&quot;&gt;File&lt;/span&gt;.join(&lt;span class=&quot;co&quot;&gt;RAILS_ROOT&lt;/span&gt;, &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;config/environment&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;    puts &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;starting scheduler at &lt;/span&gt;&lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;dl&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;co&quot;&gt;Time&lt;/span&gt;.now&lt;span class=&quot;dl&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt; for &lt;/span&gt;&lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;dl&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;co&quot;&gt;RAILS_ENV&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;co&quot;&gt;SchedulerDaemon&lt;/span&gt;.new.run&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;ensure&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    puts &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ending scheduler at &lt;/span&gt;&lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;dl&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;co&quot;&gt;Time&lt;/span&gt;.now&lt;span class=&quot;dl&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;This was actually pretty easy, and next time it will take 5 minutes to create a daemon.  Nice gem.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://work.onemanswalk.com/">
    <author>
      <name>jeremy</name>
    </author>
    <id>tag:work.onemanswalk.com,2007-05-24:395</id>
    <published>2007-05-24T05:48:00Z</published>
    <updated>2007-09-20T18:23:29Z</updated>
    <link href="http://work.onemanswalk.com/2007/5/24/more-fun-with-times-mocks-and-closures" rel="alternate" type="text/html"/>
    <title>More Fun with Times, Mocks, and Closures</title>
<content type="html">
            &lt;p&gt;I solved a complex problem in cruise today with some non-trivial mocking.  Check this one out :&lt;/p&gt;


	&lt;p&gt;I needed to test that every x amount of time, we do a clean checkout.  It can be every 6 hours, 2 days, whatever.  How do you test this?&lt;/p&gt;


	&lt;p&gt;Well, maybe you could mock the time.&lt;/p&gt;


&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;co&quot;&gt;Time&lt;/span&gt;.stubs(&lt;span class=&quot;sy&quot;&gt;:now&lt;/span&gt;).returns(&lt;span class=&quot;co&quot;&gt;Time&lt;/span&gt;.now + &lt;span class=&quot;i&quot;&gt;2&lt;/span&gt;.hours)&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;That almost works, except that the code works by touching a file.  And touch doesn&#8217;t use &lt;code&gt;Time.now&lt;/code&gt;.  Now at this point, we could go crazy, and mock &lt;code&gt;FileUtils.touch&lt;/code&gt;, of course that means we&#8217;ll also have to mock &lt;code&gt;File.exists?&lt;/code&gt; and then what are we really testing?&lt;/p&gt;


	&lt;p&gt;Instead, I used Mocha to temporarily replace &lt;code&gt;FileUtils.touch&lt;/code&gt; with my own implementation that acts like the original but uses my own value of time.  It looks like this so far.&lt;/p&gt;


&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;  marker = sandbox.root + &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;/last_clean_checkout_timestamp&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  now = &lt;span class=&quot;co&quot;&gt;Time&lt;/span&gt;.now&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;co&quot;&gt;FileUtils&lt;/span&gt;.stubs(&lt;span class=&quot;sy&quot;&gt;:touch&lt;/span&gt;).with(marker).returns(proc &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;co&quot;&gt;File&lt;/span&gt;.open(marker, &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;) {|f| f &amp;lt;&amp;lt; &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;}&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;co&quot;&gt;File&lt;/span&gt;.utime(now, now, marker)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;co&quot;&gt;Time&lt;/span&gt;.stubs(&lt;span class=&quot;sy&quot;&gt;:now&lt;/span&gt;).returns proc { now }&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;you&#8217;ll notice that both of these stubs are returning procs that reference &lt;code&gt;now&lt;/code&gt; a local variable&#8230;.&lt;/p&gt;


	&lt;p&gt;That&#8217;s ruby magic.&lt;/p&gt;


	&lt;p&gt;It means that I can now forget about mocks and write the rest of my test like this :&lt;/p&gt;


&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;12&lt;tt&gt;
&lt;/tt&gt;13&lt;tt&gt;
&lt;/tt&gt;14&lt;tt&gt;
&lt;/tt&gt;15&lt;tt&gt;
&lt;/tt&gt;16&lt;tt&gt;
&lt;/tt&gt;17&lt;tt&gt;
&lt;/tt&gt;18&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;iv&quot;&gt;@project&lt;/span&gt;.do_clean_checkout &lt;span class=&quot;sy&quot;&gt;:every&lt;/span&gt; =&amp;gt; &lt;span class=&quot;i&quot;&gt;1&lt;/span&gt;.hour&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  assert &lt;span class=&quot;iv&quot;&gt;@project&lt;/span&gt;.do_clean_checkout?&lt;tt&gt;
&lt;/tt&gt;  assert !&lt;span class=&quot;iv&quot;&gt;@project&lt;/span&gt;.do_clean_checkout?&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  now += &lt;span class=&quot;i&quot;&gt;59&lt;/span&gt;.minutes&lt;tt&gt;
&lt;/tt&gt;  assert !&lt;span class=&quot;iv&quot;&gt;@project&lt;/span&gt;.do_clean_checkout?&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  now += &lt;span class=&quot;i&quot;&gt;2&lt;/span&gt;.minutes&lt;tt&gt;
&lt;/tt&gt;  assert &lt;span class=&quot;iv&quot;&gt;@project&lt;/span&gt;.do_clean_checkout?&lt;tt&gt;
&lt;/tt&gt;  assert !&lt;span class=&quot;iv&quot;&gt;@project&lt;/span&gt;.do_clean_checkout?&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;iv&quot;&gt;@project&lt;/span&gt;.do_clean_checkout &lt;span class=&quot;sy&quot;&gt;:every&lt;/span&gt; =&amp;gt; &lt;span class=&quot;i&quot;&gt;2&lt;/span&gt;.days&lt;tt&gt;
&lt;/tt&gt;  now += &lt;span class=&quot;i&quot;&gt;1&lt;/span&gt;.day + &lt;span class=&quot;i&quot;&gt;23&lt;/span&gt;.hours&lt;tt&gt;
&lt;/tt&gt;  assert !&lt;span class=&quot;iv&quot;&gt;@project&lt;/span&gt;.do_clean_checkout?&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  now += &lt;span class=&quot;i&quot;&gt;2&lt;/span&gt;.hours&lt;tt&gt;
&lt;/tt&gt;  assert &lt;span class=&quot;iv&quot;&gt;@project&lt;/span&gt;.do_clean_checkout?&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;Instead of changing the mocks several times in between each test, I can just change my local variable &lt;code&gt;now&lt;/code&gt;.  Because of closures, the mocked out methods return the new value.&lt;/p&gt;


	&lt;p&gt;Ruby is awesome (and Mocha is pretty sweet too)&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://work.onemanswalk.com/">
    <author>
      <name>jeremy</name>
    </author>
    <id>tag:work.onemanswalk.com,2007-03-30:379</id>
    <published>2007-03-30T05:25:00Z</published>
    <updated>2007-03-30T05:26:10Z</updated>
    <link href="http://work.onemanswalk.com/2007/3/30/creating-change" rel="alternate" type="text/html"/>
    <title>Creating Change</title>
<content type="html">
            &lt;p&gt;on a recent e-mail thread, a friend asked for advice when introducing agile.  here&#8217;s a cleaned up version of my response to him.&lt;/p&gt;


	&lt;h2&gt;Solve &lt;span class=&quot;caps&quot;&gt;THEIR&lt;/span&gt; problems, don&#8217;t push your own agenda&lt;/h2&gt;


	&lt;p&gt;if you go to people and tell them agile is the key, they won&#8217;t listen to you.  instead, go to people and listen to &lt;em&gt;their&lt;/em&gt; problems.  propose and implement solutions to their biggest and worst one or two.  check back with them and make sure they are happy (or at least happier)  rinse and repeat.  if something in the &#8220;agile&#8221; toolset doesn&#8217;t scratch an itch they have, then you shouldn&#8217;t be using it.  be flexible in everything except your values.  be compromising.  if they see you give in to what they suggest, they will be more willing to give in to what you suggest.&lt;/p&gt;


	&lt;p&gt;obviously a retrospective is a great vehicle for this.  if you can get the team as a group to admit to problems that they &lt;span class=&quot;caps&quot;&gt;NOT YOU&lt;/span&gt; are worried about, then the solutions you come to will also be owned by the team.&lt;/p&gt;


	&lt;h2&gt;Start with individuals, not the &#8220;team&#8221;&lt;/h2&gt;


	&lt;p&gt;as an outsider, which we are as consultants, it&#8217;s very, very hard to convert an entire team to our way of thinking.  it&#8217;s hard to know what they all think, and the more you push, the more it becomes &lt;span class=&quot;caps&quot;&gt;YOU&lt;/span&gt; vs &lt;span class=&quot;caps&quot;&gt;THEM&lt;/span&gt;.  not good.  it&#8217;s much better if you can identify the change agents in the team, find the connectors, the mavens, and the salespeople (see &lt;a href=&quot;http://en.wikipedia.org/wiki/The_Tipping_Point&quot;&gt;The Tipping Point&lt;/a&gt; ) and talk to them.  it&#8217;s much easier to convince one person, one on one that you have some good ideas, and that you might be able to solve some of their problems.  do this first, and instead of 1 vs 10 it becomes 2 vs 9 or even 3 vs 7, &lt;span class=&quot;caps&quot;&gt;MUCH&lt;/span&gt; better odds.  if you can do this with the most influential people, then convincing the rest of the team almost takes care of itself.&lt;/p&gt;


	&lt;p&gt;this of course works the other way around, too.  the most influential people on the team can easily turn the entire team against you, so you have to make sure that you are listening to them.  addressing their problems and concerns, and making them feel heard.  this is of course good advice for the whole team, but it&#8217;s so much easier when it&#8217;s one person at a time, you might as well start there.&lt;/p&gt;


	&lt;h2&gt;Ask for help, and dish out the credit&lt;/h2&gt;


	&lt;p&gt;it&#8217;s a funny thing, but when you ask someone for help, you usually get it.  and believe me, if you&#8217;re trying to introduce agile, you&#8217;ll need all the help you can get.  the easiest help to get is advice that is asked for one on one.  ask your boss what the social makeup of the team is.  ask those teammates for advice on what the team needs.  ask someone to give a part of a presentation to management.&lt;/p&gt;


	&lt;p&gt;from their perspective, it&#8217;s a great thing to be asked for help.  it means the person asking respects your opinion about something.  it also means that they now owe you something and you&#8217;re less likely to hesitate if you need help from them.  it means that you take ownership of the thing that you helped create.&lt;/p&gt;


	&lt;p&gt;if you can manage to make people look good in exchange for your efforts, not only will you spread out the work, but you&#8217;ll win yourself friends.&lt;/p&gt;


	&lt;h2&gt;Read the &lt;a href=&quot;http://www.geraldmweinberg.com/books.html&quot;&gt;Secrets of Consulting&lt;/a&gt;&lt;/h2&gt;


	&lt;p&gt;&#8216;Nuff said.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://work.onemanswalk.com/">
    <author>
      <name>jeremy</name>
    </author>
    <id>tag:work.onemanswalk.com,2007-03-06:378</id>
    <published>2007-03-06T16:58:00Z</published>
    <updated>2007-05-24T06:05:47Z</updated>
    <link href="http://work.onemanswalk.com/2007/3/6/object-mother-in-rails" rel="alternate" type="text/html"/>
    <title>Object Mother...in rails?</title>
<content type="html">
            &lt;p&gt;That&#8217;s right, the circa 2000 pattern still makes sense today.  Use an &#8220;object mother&#8221; as a test factory to conveniently create objects for your unit tests to bang against.  It will often default values, or have different states in which to create objects.  For example, you might have a new_user, as well as a new_superuser and new_guest method all of which return users.&lt;/p&gt;


	&lt;p&gt;Read about the &lt;a href=&quot;http://www.martinfowler.com/bliki/ObjectMother.html&quot;&gt;original pattern&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;But why, you ask, not just use rails&#8217; fixtures?  Glad you asked.&lt;/p&gt;


	&lt;ol&gt;
	&lt;li&gt;it&#8217;s more intuitive and maintainable to setup the data you need right next to the test&lt;/li&gt;
		&lt;li&gt;it&#8217;s easier to create just exactly the objects you need when you have test factory methods&lt;/li&gt;
	&lt;/ol&gt;


	&lt;p&gt;But don&#8217;t take my word for it.  Let&#8217;s look at some code.&lt;/p&gt;


	&lt;p&gt;First off, what does this look like in ruby?  I am creating an &#8220;ObjectMother&#8221; module, which I then just mixin to my tests.  A test might then look like :&lt;/p&gt;


&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;  include &lt;span class=&quot;co&quot;&gt;ObjectMother&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;fu&quot;&gt;test_delete_project&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    project = new_project(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;    post &lt;span class=&quot;sy&quot;&gt;:delete&lt;/span&gt;, &lt;span class=&quot;sy&quot;&gt;:id&lt;/span&gt; =&amp;gt; project.id&lt;tt&gt;
&lt;/tt&gt;    assert_raise(&lt;span class=&quot;co&quot;&gt;ActiveRecord&lt;/span&gt;::&lt;span class=&quot;co&quot;&gt;RecordNotFound&lt;/span&gt;) { &lt;span class=&quot;co&quot;&gt;Tag&lt;/span&gt;.find(project.id)}&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;The magic is in &#8220;new_project&#8221;.  It&#8217;s an entirely pragmatic construct.  If you pass it a string, it will set everything else to acceptable defaults, and use that string as a name.  It looks something like this.&lt;/p&gt;


&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;r&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;cl&quot;&gt;ObjectMother&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;fu&quot;&gt;new_project&lt;/span&gt;(options)&lt;tt&gt;
&lt;/tt&gt;    options = {&lt;span class=&quot;sy&quot;&gt;:name&lt;/span&gt; =&amp;gt; options} &lt;span class=&quot;r&quot;&gt;if&lt;/span&gt; options.is_a? &lt;span class=&quot;co&quot;&gt;String&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    options[&lt;span class=&quot;sy&quot;&gt;:url_name&lt;/span&gt;] = options[&lt;span class=&quot;sy&quot;&gt;:name&lt;/span&gt;].gsub(&lt;span class=&quot;rx&quot;&gt;&lt;span class=&quot;dl&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;\W&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;/&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;) &lt;span class=&quot;r&quot;&gt;if&lt;/span&gt; !options.has_key?(&lt;span class=&quot;sy&quot;&gt;:url_name&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;co&quot;&gt;Project&lt;/span&gt;.create!(options)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  ...&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;Here in project, it defaults the url_name (which must be unique) from the name you&#8217;ve given it.  However, you could also create a more custom project by running this :&lt;/p&gt;


&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;  new_project(&lt;span class=&quot;sy&quot;&gt;:name&lt;/span&gt; =&amp;gt; &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;garage&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;sy&quot;&gt;:url_name&lt;/span&gt; =&amp;gt; &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;the_garage&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;sy&quot;&gt;:description&lt;/span&gt; =&amp;gt; &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;)&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;This is how it works for projects, but it&#8217;s only purpose in life is to make my life easier and reduce the amount of code one has to type or read.  So each type of thing it creates works a little bit differently according to our needs&lt;/p&gt;


&lt;hr /&gt;


	&lt;h2&gt;A more complicated example&lt;/h2&gt;


	&lt;p&gt;I was playing with ferret last week, and wrote a test that looked like this :&lt;/p&gt;


&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;r&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;fu&quot;&gt;test_across_types&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    project = new_project(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;rabbit holes&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;    post = new_post(&lt;span class=&quot;sy&quot;&gt;:subject&lt;/span&gt; =&amp;gt; &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;a rabbit has a big head&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;    user = new_user(&lt;span class=&quot;sy&quot;&gt;:display_name&lt;/span&gt; =&amp;gt; &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;rabbit head&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;iv&quot;&gt;@search&lt;/span&gt;.string = &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;rabbit&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    assert_find [project, post, user]&lt;tt&gt;
&lt;/tt&gt;    &lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;iv&quot;&gt;@search&lt;/span&gt;.string = &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;rabbit head&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    assert_find [post, user]&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;My thought process was something along the lines of :&lt;/p&gt;


	&lt;ol&gt;
	&lt;li&gt;I want to test that my searcher works across types&lt;/li&gt;
		&lt;li&gt;I need to create a project, post, and user that all have a term in them (in different places)&lt;/li&gt;
		&lt;li&gt;I want to search for the term, and make sure i get all of them&lt;/li&gt;
		&lt;li&gt;I want to search for a term that maybe 2 of them have and make sure I only get those 2&lt;/li&gt;
	&lt;/ol&gt;


	&lt;p&gt;Writing the test for this part literally took 30 seconds, I didn&#8217;t have to go lookup the fixtures or add a new fixture for my new case.  I also didn&#8217;t have to remember all the things that it takes to make a valid project or post or user.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://work.onemanswalk.com/">
    <author>
      <name>jeremy</name>
    </author>
    <id>tag:work.onemanswalk.com,2007-03-06:377</id>
    <published>2007-03-06T16:54:00Z</published>
    <updated>2007-03-18T22:25:21Z</updated>
    <link href="http://work.onemanswalk.com/2007/3/6/helpful-additions-to-test-unit" rel="alternate" type="text/html"/>
    <title>Helpful Additions To Test::Unit</title>
<content type="html">
            &lt;p&gt;Doing a lot of rails work, I&#8217;m getting a good feel for testing in ruby and rails.  Here are some tricks / snippets I use :&lt;/p&gt;


	&lt;h2&gt;assert_raises takes a string and/or a class&lt;/h2&gt;


	&lt;p&gt;I want to be able to write&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;one = Project.new('one')

projects &amp;lt;&amp;lt; one      
assert_raises('project named &quot;one&quot; already exists') do
  projects &amp;lt;&amp;lt; one
end&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;I&#8217;ve done this a few times, but I think &lt;a href=&quot;http://cruisecontrolrb.thoughtworks.com&quot;&gt;cruisecontrol.rb&#8217;s&lt;/a&gt; implementation is the most robust :&lt;/p&gt;


&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;12&lt;tt&gt;
&lt;/tt&gt;13&lt;tt&gt;
&lt;/tt&gt;14&lt;tt&gt;
&lt;/tt&gt;15&lt;tt&gt;
&lt;/tt&gt;16&lt;tt&gt;
&lt;/tt&gt;17&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;r&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;fu&quot;&gt;assert_raises&lt;/span&gt;(arg1 = &lt;span class=&quot;pc&quot;&gt;nil&lt;/span&gt;, arg2 = &lt;span class=&quot;pc&quot;&gt;nil&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;    expected_error = arg1.is_a?(&lt;span class=&quot;co&quot;&gt;Exception&lt;/span&gt;) ? arg1 : &lt;span class=&quot;pc&quot;&gt;nil&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    expected_class = arg1.is_a?(&lt;span class=&quot;co&quot;&gt;Class&lt;/span&gt;) ? arg1 : &lt;span class=&quot;pc&quot;&gt;nil&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    expected_message = arg1.is_a?(&lt;span class=&quot;co&quot;&gt;String&lt;/span&gt;) ? arg1 : arg2&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;begin&lt;/span&gt; &lt;tt&gt;
&lt;/tt&gt;      &lt;span class=&quot;r&quot;&gt;yield&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      fail &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;expected error was not raised&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;co&quot;&gt;Test&lt;/span&gt;::&lt;span class=&quot;co&quot;&gt;Unit&lt;/span&gt;::&lt;span class=&quot;co&quot;&gt;AssertionFailedError&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      raise&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;rescue&lt;/span&gt; =&amp;gt; e&lt;tt&gt;
&lt;/tt&gt;      raise &lt;span class=&quot;r&quot;&gt;if&lt;/span&gt; e.message == &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;expected error was not raised&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      assert_equal(expected_error, e) &lt;span class=&quot;r&quot;&gt;if&lt;/span&gt; expected_error&lt;tt&gt;
&lt;/tt&gt;      assert_equal(expected_class, e.class, &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Unexpected error type raised&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;) &lt;span class=&quot;r&quot;&gt;if&lt;/span&gt; expected_class&lt;tt&gt;
&lt;/tt&gt;      assert_equal(expected_message, e.message, &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Unexpected error message&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;) &lt;span class=&quot;r&quot;&gt;if&lt;/span&gt; expected_message.is_a? &lt;span class=&quot;co&quot;&gt;String&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      assert_matched(expected_message, e.message, &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Unexpected error message&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;) &lt;span class=&quot;r&quot;&gt;if&lt;/span&gt; expected_message.is_a? &lt;span class=&quot;co&quot;&gt;Regexp&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


&lt;hr /&gt;


	&lt;h2&gt;assert_equal_sets&lt;/h2&gt;


	&lt;p&gt;In Java, I used to push things into sets and compare them when I didn&#8217;t care about order.  In ruby, I sometimes use assert_equal_sets.  It does a compare of two arrays independent of order.  So&lt;/p&gt;


&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;  assert_equal_sets [&lt;span class=&quot;i&quot;&gt;1&lt;/span&gt;, &lt;span class=&quot;i&quot;&gt;3&lt;/span&gt;, &lt;span class=&quot;i&quot;&gt;5&lt;/span&gt;], [&lt;span class=&quot;i&quot;&gt;3&lt;/span&gt;, &lt;span class=&quot;i&quot;&gt;5&lt;/span&gt;, &lt;span class=&quot;i&quot;&gt;1&lt;/span&gt;]   &lt;span class=&quot;c&quot;&gt;# passes&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  assert_equal_sets [&lt;span class=&quot;i&quot;&gt;2&lt;/span&gt;, &lt;span class=&quot;i&quot;&gt;3&lt;/span&gt;], [&lt;span class=&quot;i&quot;&gt;3&lt;/span&gt;, &lt;span class=&quot;i&quot;&gt;4&lt;/span&gt;]    &lt;span class=&quot;c&quot;&gt;# fails&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;12&lt;tt&gt;
&lt;/tt&gt;13&lt;tt&gt;
&lt;/tt&gt;14&lt;tt&gt;
&lt;/tt&gt;15&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;r&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;cl&quot;&gt;Array&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;fu&quot;&gt;reorder_like!&lt;/span&gt;(other)&lt;tt&gt;
&lt;/tt&gt;    tmp = dup&lt;tt&gt;
&lt;/tt&gt;    clear&lt;tt&gt;
&lt;/tt&gt;    other.each {|x| &lt;span class=&quot;pc&quot;&gt;self&lt;/span&gt; &amp;lt;&amp;lt; tmp.delete(x) &lt;span class=&quot;r&quot;&gt;if&lt;/span&gt; tmp.index x}&lt;tt&gt;
&lt;/tt&gt;    tmp.each {|x| &lt;span class=&quot;pc&quot;&gt;self&lt;/span&gt; &amp;lt;&amp;lt; x }&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;cl&quot;&gt;Test::Unit::TestCase&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;fu&quot;&gt;assert_equal_sets&lt;/span&gt;(expected, actual)&lt;tt&gt;
&lt;/tt&gt;    actual.reorder_like!(expected)&lt;tt&gt;
&lt;/tt&gt;    assert_equal(expected, actual)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


&lt;hr /&gt;


	&lt;h2&gt;file_sandbox for testing against the file system&lt;/h2&gt;


	&lt;p&gt;After dragging this code around me for the last 6 or 7 projects I&#8217;ve been on, I finally packaged it up as a &lt;a href=&quot;http://rubyforge.org/projects/file_sandbox&quot;&gt;gem&lt;/a&gt; .  It lets you write code like :&lt;/p&gt;


&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;in_sandbox &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt; |sandbox|&lt;tt&gt;
&lt;/tt&gt;  sandbox.new &lt;span class=&quot;sy&quot;&gt;:file&lt;/span&gt; =&amp;gt; &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;b/a.txt&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;sy&quot;&gt;:with_contents&lt;/span&gt; =&amp;gt; &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;some stuff&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  assert_equal &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;some_stuff&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;co&quot;&gt;File&lt;/span&gt;.read(sandbox.root + &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;/b/a.txt&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;Basically it creates a temporary directory for you to muck about in.  After the block is ended (or teardown is called on your test) that directory and everything in it is guaranteed to be cleaned up.  It also has a bunch of methods to make file based things easier like creating a file, etc.&lt;/p&gt;


	&lt;p&gt;Install it with &#8220;gem install file_sandbox&#8221;&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://work.onemanswalk.com/">
    <author>
      <name>jeremy</name>
    </author>
    <id>tag:work.onemanswalk.com,2007-01-29:44</id>
    <published>2007-01-29T19:14:00Z</published>
    <updated>2007-03-18T22:25:21Z</updated>
    <link href="http://work.onemanswalk.com/2007/1/29/accessing-private-data-in-ruby" rel="alternate" type="text/html"/>
    <title>Accessing Private Data in Ruby</title>
<content type="html">
            &lt;p&gt;Of &lt;em&gt;course&lt;/em&gt; that&#8217;s how you do it&#8230;&lt;/p&gt;


&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;user.instance_eval(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;@password&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;Every now and then you really do need to access a private variable and don&#8217;t particularly want to change the class.  Especially in test code.  Maybe it&#8217;s a smell, but I&#8217;d resorted to reopening classes and adding attr_accessor&#8217;s.&lt;/p&gt;


	&lt;p&gt;Moral issues aside, this is definitely a more elegant way of doing it.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://work.onemanswalk.com/">
    <author>
      <name>jeremy</name>
    </author>
    <id>tag:work.onemanswalk.com,2006-12-20:43</id>
    <published>2006-12-20T19:42:21Z</published>
    <updated>2007-03-18T22:25:21Z</updated>
    <link href="http://work.onemanswalk.com/2006/12/20/setting-today-in-a-test" rel="alternate" type="text/html"/>
    <title>Setting Today in a Test</title>
<content type="html">
            Ruby is awesome&#8230;
&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;r&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;fu&quot;&gt;test_on_weekday&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    today_is &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;May 3, 2006&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      calendar.add(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;a call&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;).at(&lt;span class=&quot;i&quot;&gt;4&lt;/span&gt;.pm).on(&lt;span class=&quot;sy&quot;&gt;:saturday&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;      assert_equal(&lt;span class=&quot;co&quot;&gt;Time&lt;/span&gt;.local(&lt;span class=&quot;i&quot;&gt;2006&lt;/span&gt;, &lt;span class=&quot;i&quot;&gt;5&lt;/span&gt;, &lt;span class=&quot;i&quot;&gt;6&lt;/span&gt;, &lt;span class=&quot;i&quot;&gt;16&lt;/span&gt;),&lt;tt&gt;
&lt;/tt&gt;                 calendar.appointments.first.start)&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;...playing with code examples for our book.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://work.onemanswalk.com/">
    <author>
      <name>jeremy</name>
    </author>
    <id>tag:work.onemanswalk.com,2006-12-20:42</id>
    <published>2006-12-20T18:22:04Z</published>
    <updated>2007-03-18T22:25:21Z</updated>
    <link href="http://work.onemanswalk.com/2006/12/20/stand-ups-as-huddles" rel="alternate" type="text/html"/>
    <title>Stand ups as Huddles</title>
<content type="html">
            &lt;p&gt;We&#8217;ve been talking about stand ups here at ThoughtWorks.&lt;/p&gt;


	&lt;p&gt;I&#8217;m actually not a big fan of the traditional yesterday &#8211; today &#8211; issues format.  I find that too often it becomes a status meeting &#8211; this is what I did yesterday, doing more of the same today, and no issues.&lt;/p&gt;


	&lt;p&gt;Instead of a status meeting, I like to treat it as a planning meeting.  I prefer to think of a stand up like a huddle in American football.  The point is to focus on today, and figure out a game plan that makes the most sense.  Who needs to pair with whom?  Who&#8217;s tackling what stories?&lt;/p&gt;


	&lt;p&gt;I find the signal to noise ratio is much higher in the latter format as is the energy.  It&#8217;s fine to mention anything from yesterday that is especially pertinent or interesting to the team, but I think the focus should be on today.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://work.onemanswalk.com/">
    <author>
      <name>jeremy</name>
    </author>
    <id>tag:work.onemanswalk.com,2006-12-08:41</id>
    <published>2006-12-08T05:55:22Z</published>
    <updated>2007-03-18T22:25:21Z</updated>
    <link href="http://work.onemanswalk.com/2006/12/8/effective-writing" rel="alternate" type="text/html"/>
    <title>Effective Writing</title>
<content type="html">
            &lt;p&gt;Our publishers have given us a deadline of the end of the year to get 60 pages out for &lt;strong&gt;DSLs in Ruby&lt;/strong&gt;.  That&#8217;s not a lot of time, but honestly probably a really great thing for us.  We needed the push.&lt;/p&gt;


	&lt;p&gt;Putting everything else on hold for the month, I&#8217;ve found myself scrambling to find a way to apply all the discipline that I have when programming to writing.   It&#8217;s hard, and getting started is always the hardest part.&lt;/p&gt;


	&lt;p&gt;I&#8217;ve gone through many days of &#8220;working&#8221; yet getting nothing done.  This is what I&#8217;ve learned, it&#8217;s working for me, maybe it will work for you :&lt;/p&gt;


&lt;hr /&gt;


	&lt;h3&gt;Work top down&lt;/h3&gt;


	&lt;p&gt;I remember outlining when I was writing papers in school.  I never particularly cared for it.  I could pretty much keep the structure of my paper in my head and just write from that.  Turns out that&#8217;s not actually true for a book.  And it&#8217;s even less true when you are writing a book with 4 authors in 3 different timezones.&lt;/p&gt;


	&lt;p&gt;Get a loose outline for the whole book, and then as you write each chapter, outline that in detail.  Work on one chapter at a time.&lt;/p&gt;


	&lt;h3&gt;Question Driven Writing&lt;/h3&gt;


	&lt;p&gt;XP has &lt;span class=&quot;caps&quot;&gt;TDD&lt;/span&gt; to keep you honest.  If it&#8217;s not making a test pass, you should delete it or write a test for it.&lt;/p&gt;


	&lt;p&gt;In a similar way, why not start a chapter by writing the questions it should answer.  If you find yourself writing text that doesn&#8217;t answer one of those questions, delete it, or add the question.&lt;/p&gt;


	&lt;h3&gt;Work in Sprints&lt;/h3&gt;


	&lt;p&gt;I find I simply &lt;span class=&quot;caps&quot;&gt;CAN NOT&lt;/span&gt; concentrate if I&#8217;m online and checking mail and talking to my roomates and answering IM&#8217;s&#8230;  As damaging as switching context is when I&#8217;m coding, it&#8217;s several times more damaging when I&#8217;m writing.&lt;/p&gt;


	&lt;p&gt;The problem is that there&#8217;s always more to learn, so it&#8217;s way easy to get distracted.  Oh, Joe&#8217;s online, let&#8217;s see if he knows about declarative programming.  Let&#8217;s google for metaprogramming.  Let&#8217;s download rBehave.&lt;/p&gt;


	&lt;p&gt;Even working on examples can be a rabbit hole.  And with no pair to pull me back to what we should be doing, I&#8217;ve wasted weeks on not important stuff.&lt;/p&gt;


	&lt;p&gt;The solution?&lt;/p&gt;


	&lt;p&gt;Figure out what needs to get done next.  Do some outlining or research or write some examples, whatever.  Once you&#8217;re confident that you have some material to write about, start a sprint.&lt;/p&gt;


	&lt;p&gt;I&#8217;m using 2 hour increments, but I could probably do 3 or 4.&lt;/p&gt;


	&lt;ol&gt;
	&lt;li&gt;Unplug the wifi, evdo or lan cable.&lt;/li&gt;
		&lt;li&gt;Take a minute to think about what you&#8217;re about to do (call it a mini standup) tell someone if, say your girlfriend&#8217;s available&lt;/li&gt;
		&lt;li&gt;Set your timer for 2 hours and write.&lt;/li&gt;
	&lt;/ol&gt;


	&lt;p&gt;The rules are you have to be making forward progress.  If you come up against any hurdles, want to ask someone a question, park it.  Write it down on a card or a &#8220;todo.txt&#8221; file.&lt;/p&gt;


	&lt;p&gt;What do you do when you write?&lt;/p&gt;
          </content>  </entry>
</feed>
