<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/atom10full.xsl" type="text/xsl" media="screen"?><?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/itemcontent.css" type="text/css" media="screen"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" xml:lang="en-US">
  <title>Rails in the Country - Home</title>
  <id>tag:blog.allen.com.au,2008:mephisto/</id>
  <generator uri="http://mephistoblog.com" version="0.7.2">Mephisto Noh-Varr</generator>
  
  <link href="http://blog.allen.com.au/" rel="alternate" type="text/html" />
  <updated>2008-06-25T13:30:10Z</updated>
  <link rel="self" href="http://feeds.feedburner.com/RailsInTheCountry" type="application/atom+xml" /><entry xml:base="http://blog.allen.com.au/">
    <author>
      <name>Matt Allen</name>
    </author>
    <id>tag:blog.allen.com.au,2008-06-24:37</id>
    <published>2008-06-24T22:53:00Z</published>
    <updated>2008-06-25T13:30:10Z</updated>
    <link href="http://feeds.feedburner.com/~r/RailsInTheCountry/~3/319234272/rails-camp-the-third" rel="alternate" type="text/html" />
    <title>Rails Camp, the Third</title>
<content type="html">
            &lt;p&gt;Here we are, a few days after the &lt;a href="http://railscamp08.org/"&gt;Third Rails Camp&lt;/a&gt; in Australia.&lt;/p&gt;


	&lt;p&gt;This was my first true rails camp, I attended a day of the first one but due to having a 2 week old baby 4 days/ 3 nights was out of the question. When the 2nd one in Melbourne was on, I was in Fiji with my family, I can’t complain too loudly.&lt;/p&gt;


	&lt;p&gt;Rails Camp was just awesome. The vibe was electric. It was great putting names to faces with the guys from the #roro &lt;span class="caps"&gt;IRC&lt;/span&gt; channel that I spend alot of time in. We asked people to blog about Rails Camp so we could get the word out, here is what I hacked on:&lt;/p&gt;


	&lt;h2&gt;Twetter&lt;/h2&gt;


	&lt;p&gt;Whilst shivering my arse off in bed on Friday night it hit me – how hard would it be to make tiwtter.com resolve to my laptop and watch the logs for the URLs? The answer was, not hard at all. So first thing on Saturday morning I had twitteriffic running locally. It took 50 lines of code in a rails 2.1 app. &lt;a href="http://blog.smartbomb.com.au/"&gt;Lachie&lt;/a&gt; had an example of the &lt;span class="caps"&gt;XML&lt;/span&gt; that the &lt;span class="caps"&gt;API&lt;/span&gt; returned and with a little help from Max M and his internet enabled CrackBerry we were able to read the twitter &lt;span class="caps"&gt;API&lt;/span&gt; docs and get the other bits and pieces. Once &lt;a href="http://lstoll.net/"&gt;Lincoln&lt;/a&gt; woke up I had twitter.com resolving to a shiny new VM on Bigguns (the awesome server) and twitter was alive! There was no friending, everyone got the same stream. I did implement replies but left out direct messages. I think it was a hit.&lt;/p&gt;


	&lt;h2&gt;Merb Caching&lt;/h2&gt;


	&lt;p&gt;I met &lt;a href="http://hassox.blogspot.com/" title="hassox"&gt;Dan Neighman&lt;/a&gt; for the first time in real life. We speak alot on &lt;span class="caps"&gt;IRC&lt;/span&gt; and hang our in #geekdads. He was working on a new caching mechanism for Merb and came to seek me out as the resident Caching Guru. I was flattered.  We spent a good few hours over the weekend fleshing out the solutions to problems that I currently have solved in rails in a hacky, need to use cron kind of way. The outcome was &lt;a href="http://wiki.merbivore.com/pages/background-tasks"&gt;background-tasks&lt;/a&gt;. Dan wrote it up &lt;a href="http://merbunity.com/news/28"&gt;here&lt;/a&gt;. This is Uber exciting and I really want to start Merb Hacking.&lt;/p&gt;


	&lt;h3&gt;Managing Expectations&lt;/h3&gt;


	&lt;p&gt;I decided to give a talk on Managing Expectations. In a previous life this was what our software did but in reality we all have to do it each day. I pegged it as a discussion rather than a talk and that was exactly how it panned out. I reckon there might have been 20 guys all sitting around relating stories about their experiences and how they dealt with them.  I think it’s safe to say that the conclusion was, politics aside, that being open and transparent is the safest way forward, especially if you’re a one-man-band or a small shop.  Clients/Bosses are generally more willing to co-operate if you play it straight.  The talk was actually interrupted by lunch but quite a large proportion actually kept talking whilst eating. I think this was a great outcome for a non-tech talk at a very geeky weekend.&lt;/p&gt;


	&lt;p&gt;So, to all you guys that read this and went, blog it up people! Get it out there. Let’s start a * Camp movement.&lt;/p&gt;


	&lt;p&gt;—Matta&lt;/p&gt;
          </content>  <feedburner:origLink>http://blog.allen.com.au/2008/6/24/rails-camp-the-third</feedburner:origLink></entry>
  <entry xml:base="http://blog.allen.com.au/">
    <author>
      <name>Matt Allen</name>
    </author>
    <id>tag:blog.allen.com.au,2008-05-05:32</id>
    <published>2008-05-05T21:49:00Z</published>
    <updated>2008-05-05T21:50:22Z</updated>
    <link href="http://feeds.feedburner.com/~r/RailsInTheCountry/~3/284219155/going-dark-again" rel="alternate" type="text/html" />
    <title>Going Dark. Again</title>
<content type="html">
            &lt;p&gt;&lt;span class="caps"&gt;UPDATE&lt;/span&gt;: &lt;a href="http://30sleeps.com/blog/2008/01/31/dealing-with-information-overload/"&gt;I reckon this article has nailed it&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;This weekend was the Anzac weekend, a time for reflection and remembering the things our Grand Fathers and Great Grand Fathers (and possibly Mothers) did for us and our way of life. I miss you Pa.&lt;/p&gt;


	&lt;p&gt;I had the opportunity on Friday night to sit down with some code, uninterrupted. No internet, therefore no Email, IM, &lt;span class="caps"&gt;IRC&lt;/span&gt;, Twitter, &lt;span class="caps"&gt;RSS&lt;/span&gt;. It was &lt;strong&gt;grand&lt;/strong&gt;.&lt;/p&gt;


	&lt;p&gt;It started me thinking, how the hell can us “knowledge” (I hate that term) workers balance being as productive as you know you can with keeping in touch?  The vast majority of my friends are online most of the time, at work, at home and even in between sometimes (Hi Lachlan!). I like being in the loop, I like knowing what’s going on. Twitter has made that a process alot easier than it was 12 months ago.&lt;/p&gt;


	&lt;p&gt;So I have decided that my work day is now going to consist mostly of “off-line” time. 9 – Lunch will be offline, that is, working without distractions. No Email, &lt;span class="caps"&gt;IRC&lt;/span&gt;, IM, Twitter. At Lunch time i’ll surface again for a little while, probably an hour tops. After lunch i’ll hook back in.  After the kids are in bed I may come back on, maybe not. Dunno yet.&lt;/p&gt;


	&lt;p&gt;I tend to embrace things half-heartedly, this is one thing i’m going to try and stick to. If you need me urgently, &lt;span class="caps"&gt;SMS&lt;/span&gt; or Phone is where it’s at.&lt;/p&gt;


	&lt;p&gt;The other side effect I hope this will address is my new level of bored-ness with the interwebs, seriously, I can’t find anything interesting out there atm.&lt;/p&gt;


	&lt;p&gt;Apologies for the above brain dump, upon re-reading it wasn’t very well thought out. Oh Well.&lt;/p&gt;


	&lt;p&gt;Matta&lt;/p&gt;
          </content>  <feedburner:origLink>http://blog.allen.com.au/2008/5/5/going-dark-again</feedburner:origLink></entry>
  <entry xml:base="http://blog.allen.com.au/">
    <author>
      <name>Matt Allen</name>
    </author>
    <id>tag:blog.allen.com.au,2007-12-17:31</id>
    <published>2007-12-17T22:21:00Z</published>
    <updated>2008-03-30T22:05:39Z</updated>
    <link href="http://feeds.feedburner.com/~r/RailsInTheCountry/~3/201873834/fluid-app" rel="alternate" type="text/html" />
    <title>Fluid.app</title>
<content type="html">
            &lt;p&gt;Have been using &lt;a href="http://fluidapp.com"&gt;Fluid&lt;/a&gt; for the past week.&lt;/p&gt;


	&lt;p&gt;It’s an &lt;span class="caps"&gt;OS X&lt;/span&gt; (Leopard only) app that lets you create standalone cocoa based web apps, basically a wrapper for web kit.&lt;/p&gt;


	&lt;p&gt;I’ve got one for gmail and unfuddle setup. It’s nice to have them survive browser crashes normally due to unruley javascript.&lt;/p&gt;


	&lt;p&gt;&lt;img src="http://blog.allen.com.au/assets/2007/12/17/Picture_49.png" alt="" /&gt;&lt;/p&gt;


	&lt;p&gt;&lt;img src="http://blog.allen.com.au/assets/2007/12/17/Picture_50.png" alt="" /&gt;&lt;/p&gt;


	&lt;p&gt;&lt;span class="caps"&gt;UPDATE&lt;/span&gt;: Here’s a sexy big icon the UF guys sent me&lt;/p&gt;


	&lt;p&gt;&lt;img src="http://blog.allen.com.au/assets/2008/3/30/widget.icon.128.png" alt="" /&gt;&lt;/p&gt;
          </content>  <feedburner:origLink>http://blog.allen.com.au/2007/12/17/fluid-app</feedburner:origLink></entry>
  <entry xml:base="http://blog.allen.com.au/">
    <author>
      <name>Matt Allen</name>
    </author>
    <id>tag:blog.allen.com.au,2007-12-06:28</id>
    <published>2007-12-06T08:23:00Z</published>
    <updated>2007-12-22T20:34:13Z</updated>
    <category term="Rails" />
    <link href="http://feeds.feedburner.com/~r/RailsInTheCountry/~3/195978974/highly-resposive-unfuddlers" rel="alternate" type="text/html" />
    <title>Highly resposive Unfuddlers</title>
<content type="html">
            &lt;p&gt;To follow on from my last post, I have spent the day to-ing and fro-ing with the Unfuddle.com guys and I have come up with an &lt;span class="caps"&gt;SVN&lt;/span&gt; hook for non-UF-hosted svn repos.&lt;/p&gt;


	&lt;p&gt;&lt;a href="http://unfuddle.com/community/forums/6/topics/105"&gt;Here’s for code for anyone interested&lt;/a&gt;&lt;/p&gt;
          </content>  <feedburner:origLink>http://blog.allen.com.au/2007/12/6/highly-resposive-unfuddlers</feedburner:origLink></entry>
  <entry xml:base="http://blog.allen.com.au/">
    <author>
      <name>Matt Allen</name>
    </author>
    <id>tag:blog.allen.com.au,2007-11-30:27</id>
    <published>2007-11-30T02:19:00Z</published>
    <updated>2007-12-22T20:34:03Z</updated>
    <category term="Rails" />
    <link href="http://feeds.feedburner.com/~r/RailsInTheCountry/~3/192723366/move-over-lighthouse-unfuddle-it-is" rel="alternate" type="text/html" />
    <title>Move over lighthouse, Unfuddle it is</title>
<content type="html">
            &lt;p&gt;So, I blogged earlier about Lighthouse and how much I was in love it with. It has all the features I like but unfortunately for me, it missed a few critical ones my Boss wanted.&lt;/p&gt;


	&lt;p&gt;Enter &lt;a href="http://unfuddle.com/home"&gt;Unfuddle&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;We’ve been using this for a few months now and it works a charm. It’s a rails app and is run buy some very passionate dudes in Hawaii.&lt;/p&gt;


	&lt;p&gt;The feature that really stands out for us is the work flow for tickets. See image below.&lt;/p&gt;


	&lt;p&gt;&lt;a href="http://blog.allen.com.au/assets/2007/11/30/Picture_47.png"&gt;&lt;img src="http://blog.allen.com.au/assets/2007/11/30/Picture_47_thumb_1.png" /&gt;&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;Boss opens it, I accept it, I resolve it, boss closes it.&lt;/p&gt;


	&lt;p&gt;Boss can reopen it if need be. So easy for me to see what tickets are left to work on in this release, also what tickets need to be verified by him before I can push the release live.&lt;/p&gt;


	&lt;p&gt;Anyone who is dealing with a large ongoing site needs a decent bug tracker, this one is up there with the best i’ve used, both &lt;span class="caps"&gt;OSS&lt;/span&gt; and commercial.&lt;/p&gt;
          </content>  <feedburner:origLink>http://blog.allen.com.au/2007/11/30/move-over-lighthouse-unfuddle-it-is</feedburner:origLink></entry>
  <entry xml:base="http://blog.allen.com.au/">
    <author>
      <name>Matt Allen</name>
    </author>
    <id>tag:blog.allen.com.au,2007-11-27:26</id>
    <published>2007-11-27T01:55:00Z</published>
    <updated>2007-12-15T02:39:52Z</updated>
    <link href="http://feeds.feedburner.com/~r/RailsInTheCountry/~3/191024304/print-this-out-give-it-to-your-boss" rel="alternate" type="text/html" />
    <title>Print this out, give it to your boss</title>
<content type="html">
            &lt;p&gt;I don’t actually have to, he sent me the link.&lt;/p&gt;


	&lt;p&gt;This made me a very happy programmer.&lt;/p&gt;


	&lt;p&gt;&lt;a href="http://blog.eod.com/post/18462877"&gt;Wide vs. Deep&lt;/a&gt;&lt;/p&gt;
          </content>  <feedburner:origLink>http://blog.allen.com.au/2007/11/27/print-this-out-give-it-to-your-boss</feedburner:origLink></entry>
  <entry xml:base="http://blog.allen.com.au/">
    <author>
      <name>Matt Allen</name>
    </author>
    <id>tag:blog.allen.com.au,2007-10-17:23</id>
    <published>2007-10-17T01:25:00Z</published>
    <updated>2007-10-17T01:25:37Z</updated>
    <link href="http://feeds.feedburner.com/~r/RailsInTheCountry/~3/170912739/going-dark" rel="alternate" type="text/html" />
    <title>Going Dark</title>
<content type="html">
            &lt;p&gt;Not sure if it’s just me, but lately the constant barrage of IM/IRC/Twitter/Email has been making me feel less productive than I know I can be so for the majority of the day I have been Going Dark. That is, turn comms off and the music up.&lt;/p&gt;


	&lt;p&gt;I don’t go completely off net as I need &lt;a href="http://google.com.au"&gt;my other brain&lt;/a&gt; to remind me of things I constantly forget.&lt;/p&gt;


	&lt;p&gt;I miss my &lt;span class="caps"&gt;IRC&lt;/span&gt; buddies from #roro on freenode.net heaps, they are a constant source of knowledge and entertainment but they are pretty good at posting to the &lt;a href="http://roro.tumblr.com/"&gt;roro tumblr&lt;/a&gt;, so that keeps me in the loop mostly.&lt;/p&gt;


	&lt;p&gt;I actually jump on IM and &lt;span class="caps"&gt;IRC&lt;/span&gt; once or twice during the day, mostly at lunch, or in a lull between pushing out code.&lt;/p&gt;


	&lt;p&gt;I’d like to hear how everyone else deals with the “always on” side of things?&lt;/p&gt;
          </content>  <feedburner:origLink>http://blog.allen.com.au/2007/10/17/going-dark</feedburner:origLink></entry>
  <entry xml:base="http://blog.allen.com.au/">
    <author>
      <name>Matt Allen</name>
    </author>
    <id>tag:blog.allen.com.au,2007-10-15:22</id>
    <published>2007-10-15T06:34:00Z</published>
    <updated>2007-10-15T06:35:22Z</updated>
    <link href="http://feeds.feedburner.com/~r/RailsInTheCountry/~3/170006602/when-testing-caching" rel="alternate" type="text/html" />
    <title>When Testing Caching ...</title>
<content type="html">
            &lt;p&gt;Don’t forget to set:&lt;/p&gt;


	&lt;p&gt;config.action_controller.perform_caching = true&lt;/p&gt;


	&lt;p&gt;in test.rb in environments. You’ll wonder why nothing is working until you do.&lt;/p&gt;
          </content>  <feedburner:origLink>http://blog.allen.com.au/2007/10/15/when-testing-caching</feedburner:origLink></entry>
  <entry xml:base="http://blog.allen.com.au/">
    <author>
      <name>Matt Allen</name>
    </author>
    <id>tag:blog.allen.com.au,2007-09-17:21</id>
    <published>2007-09-17T11:21:00Z</published>
    <updated>2007-09-17T11:23:05Z</updated>
    <link href="http://feeds.feedburner.com/~r/RailsInTheCountry/~3/157557751/33-702635-151-099434-hornsby" rel="alternate" type="text/html" />
    <title>-33.702635,151.099434 : Hornsby</title>
<content type="html">
            &lt;p&gt;So what does a north western Sydney suburb have to do with rails? Not very much, but it has lent it’s name to a very very cool app written by &lt;a href="http://smartbomb.com.au"&gt;Lachie Cox&lt;/a&gt;.&lt;/p&gt;


	&lt;p&gt;The main aim of &lt;a href="http://rails-oceania.googlecode.com/svn/lachiecox/hornsby/"&gt;Hornsby&lt;/a&gt; is to get rid of the dreaded rails fixtures. As a newcomer to Rails about a year ago, testing was a new and exciting thing for me.  It took a bit of discipline but I got into it. Then I got out of it. Fixtures where p*ssing me off, badly. They were a nightmare to maintain and actually made me write worse code than normal due to the fact I didn’t want to rewrite all my fixtures.&lt;/p&gt;


	&lt;p&gt;I then started using Rspec, I love writing specs but fixtures were still making me swear at my &lt;span class="caps"&gt;MBP&lt;/span&gt;.&lt;/p&gt;


	&lt;p&gt;Hornsby makes me an extremely happy Rails programmer. Here is &lt;a href="http://blog.smartbomb.com.au/2007/8/29/hornsby"&gt;Lachie’s description&lt;/a&gt;.&lt;/p&gt;


	&lt;p&gt;Here’s my code. It’s an example of setting up a “scenario” that I used for testing my Equipment&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&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="code"&gt;&lt;pre&gt;scenario &lt;span class="sy"&gt;:equipment&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="iv"&gt;@manufacturer&lt;/span&gt; = &lt;span class="co"&gt;EquipmentManufacturer&lt;/span&gt;.create!(&lt;span class="sy"&gt;:name&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Ben Hogan&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="iv"&gt;@category&lt;/span&gt; = &lt;span class="co"&gt;EquipmentCategory&lt;/span&gt;.create!(&lt;span class="sy"&gt;:title&lt;/span&gt; =&amp;gt;&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Fairway Woods&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="iv"&gt;@equipment&lt;/span&gt; = &lt;span class="co"&gt;EquipmentProduct&lt;/span&gt;.new(&lt;span class="sy"&gt;:name&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Edge CFT Ti  Hybrid&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;,&lt;span class="sy"&gt;:equipment_manufacturer_id&lt;/span&gt; =&amp;gt; &lt;span class="iv"&gt;@manufacturer&lt;/span&gt;.id,&lt;span class="sy"&gt;:equipment_category_id&lt;/span&gt; =&amp;gt; &lt;span class="iv"&gt;@category&lt;/span&gt;.id)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="iv"&gt;@equipment&lt;/span&gt;.save(&lt;span class="pc"&gt;false&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;Here is the bit from Rspec test that loads it up.&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&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="code"&gt;&lt;pre&gt;describe &lt;span class="co"&gt;EquipmentController&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  before(&lt;span class="sy"&gt;:each&lt;/span&gt;) &lt;span class="r"&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    hornsby_scenario &lt;span class="sy"&gt;:equipment&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;   ...&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;You’ll notice I hand in “false” to the save method, this allows us to bypass all the validations and setup just the bare minimum to pass tests. This is helpful sometimes but be aware it can lead to other weird stuff than you might need to be aware of, assuming your validations are setup for a reason. Another nice side effect of Hornsby scenarios is the fact that if your DB tables are not the rails default ones you don’t have to jump though hoops to get your Db populated.&lt;/p&gt;


	&lt;p&gt;This software has made my testing life a joy yet again, knowing the exact state of your DB when testing is precisely what was missing from fixtures.&lt;/p&gt;


	&lt;p&gt;Here’s those links again:&lt;/p&gt;


	&lt;p&gt;&lt;a href="http://blog.smartbomb.com.au/2007/8/29/hornsby"&gt;Lachie’s write up&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;&lt;a href="http://rails-oceania.googlecode.com/svn/lachiecox/hornsby/"&gt;Source Code&lt;/a&gt;&lt;/p&gt;
          </content>  <feedburner:origLink>http://blog.allen.com.au/2007/9/17/33-702635-151-099434-hornsby</feedburner:origLink></entry>
  <entry xml:base="http://blog.allen.com.au/">
    <author>
      <name>Matt Allen</name>
    </author>
    <id>tag:blog.allen.com.au,2007-08-28:12</id>
    <published>2007-08-28T21:56:00Z</published>
    <updated>2007-08-29T07:04:14Z</updated>
    <link href="http://feeds.feedburner.com/~r/RailsInTheCountry/~3/149540437/out-of-bandwidth-cache-expiry" rel="alternate" type="text/html" />
    <title>Out of Bandwidth Cache Expiry</title>
<content type="html">
            &lt;p&gt;So, you’ve been merrily caching away and using the cache plugin I wrote and you’re still getting some heat about your site sometimes “hanging”. I reckon the problem might be that the person complaining is getting stuck behind a slow running process that is re-caching a page on your site. So what to do?&lt;/p&gt;


	&lt;p&gt;What I’ve done is started an extra mongrel in my pack but not included it in the standard mongrel pack, my (cut down) mongrel config looks like this:&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&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="code"&gt;&lt;pre&gt;--- &lt;tt&gt;
&lt;/tt&gt;port: &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;2222&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;environment: production&lt;tt&gt;
&lt;/tt&gt;address: &lt;span class="fl"&gt;127.0&lt;/span&gt;.&lt;span class="fl"&gt;0.1&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;pid_file: log/mongrel.pid&lt;tt&gt;
&lt;/tt&gt;servers: &lt;span class="i"&gt;7&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;As you can see there is 7 mongrels there. Next, setup a second cluster that contains only your 7th mongrel&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&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="code"&gt;&lt;pre&gt;&amp;lt;&lt;span class="co"&gt;Proxy&lt;/span&gt; balancer&lt;span class="sy"&gt;:/&lt;/span&gt;/expirecluster&amp;gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="co"&gt;BalancerMember&lt;/span&gt; http&lt;span class="sy"&gt;:/&lt;/span&gt;/&lt;span class="fl"&gt;127.0&lt;/span&gt;.&lt;span class="fl"&gt;0.1&lt;/span&gt;:&lt;span class="i"&gt;2228&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&amp;lt;&lt;span class="rx"&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;span class="k"&gt;Proxy&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;Next, tell apache to redirect any of your magic expiration URLs to that cluster&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&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="code"&gt;&lt;pre&gt;  &lt;span class="co"&gt;RewriteCond&lt;/span&gt;  &lt;span class="s"&gt;&lt;span class="dl"&gt;%{&lt;/span&gt;&lt;span class="k"&gt;QUERY_STRING&lt;/span&gt;&lt;span class="dl"&gt;}&lt;/span&gt;&lt;/span&gt; magic_url_expire_param=&lt;span class="i"&gt;1&lt;/span&gt;&lt;span class="er"&gt;$&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="co"&gt;RewriteRule&lt;/span&gt; ^&lt;span class="rx"&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;span class="k"&gt;(.*)$ balancer:&lt;/span&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;/span&gt;/expirecluster%{&lt;span class="co"&gt;REQUEST_URI&lt;/span&gt;} [&lt;span class="co"&gt;P&lt;/span&gt;,&lt;span class="co"&gt;QSA&lt;/span&gt;,&lt;span class="co"&gt;L&lt;/span&gt;]&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;Restart apache and you’re done. Any cron triggered expire requests will now go through the out of bandwith mongrel and not slow down visitors to you site.&lt;/p&gt;


	&lt;p&gt;This method can actually be used to segregate any URLs off to different clusters, I have a project coming up that will have a lot of slow processes that rely on an external &lt;span class="caps"&gt;SOAP&lt;/span&gt; service running.  They’ll all be farmed off to a cluster running on a totally different machine with trimmed down, DB free mongrels.&lt;/p&gt;
          </content>  <feedburner:origLink>http://blog.allen.com.au/2007/8/28/out-of-bandwidth-cache-expiry</feedburner:origLink></entry>
  <entry xml:base="http://blog.allen.com.au/">
    <author>
      <name>Matt Allen</name>
    </author>
    <id>tag:blog.allen.com.au,2007-08-21:10</id>
    <published>2007-08-21T12:37:00Z</published>
    <updated>2007-08-21T12:53:20Z</updated>
    <link href="http://feeds.feedburner.com/~r/RailsInTheCountry/~3/146496171/seesaw-restarting-your-mongrels-with-zero-downtime" rel="alternate" type="text/html" />
    <title>Seesaw, restarting your mongrels with zero downtime</title>
<content type="html">
            &lt;p&gt;Last weekend was the first Sydney Rails Group Hax Day, held by our very own &lt;a href="http://smartbomb.com.au"&gt;Lachie&lt;/a&gt;. What a day! about 10 rails geeks showed up including a few guys we had either never met or only at the previous meet-up.&lt;/p&gt;


	&lt;p&gt;&lt;a href="http://synaphy.com"&gt;Max&lt;/a&gt; and I started the day talking about what we’d like to see in a web-based app that managed other rails apps.  In discussing this I mentioned that over that &lt;a href="http://iseekgolf.com"&gt;iseekgolf.com&lt;/a&gt; we’re running getting a serious amount of traffic and a lot of that is e-commerce related, i.e stuff that would really screw me up if the mongrel is was being executed on was killed half way though a process.  This led to me suggesting that it’d be great if you could restart mongrels bit by bit.&lt;/p&gt;


	&lt;p&gt;After about 30 mins of talking it though we decided that the best way was to split the mongrel pack down the middle and bounce each half separately while the other half handled the load. This would potentially slow the site down however it shouldn’t shut the site down entirely.&lt;/p&gt;


	&lt;p&gt;What we came up with was &lt;a href="http://rubyforge.org/frs/shownotes.php?group_id=3286&amp;release_id=13892"&gt;Seesaw&lt;/a&gt;. This does precisely what we needed including configuring the web server on the fly.&lt;/p&gt;


	&lt;p&gt;This morning I installed it live at iseekgolf.com and after a few config tweaks it was working a treat. It brought a huge smile to my face as the logs kept rolling buy as the mongrels were restarting.&lt;/p&gt;


	&lt;p&gt;One gotcha is that by default Seesaw names your mongrel cluster “mongrel_pack”, be sure to replace &lt;strong&gt;all&lt;/strong&gt; instances of your mongrel cluster name in your apache config file to this new name, especially the &lt;span class="caps"&gt;SSL&lt;/span&gt; one ;)&lt;/p&gt;


	&lt;p&gt;Hopefully some people may find a use for this plugin.&lt;/p&gt;


	&lt;p&gt;We’ll be doing some minor changes to it over the coming days, including some definable pauses in between the cluster restarts and apache redirecting traffic to them to give them time to warm up.&lt;/p&gt;


	&lt;p&gt;Max has written up an &lt;a href="http://synaphy.com/2007/8/20/seesaw"&gt;awesome blog entry&lt;/a&gt;, with pretty pictures and all!&lt;/p&gt;
          </content>  <feedburner:origLink>http://blog.allen.com.au/2007/8/21/seesaw-restarting-your-mongrels-with-zero-downtime</feedburner:origLink></entry>
  <entry xml:base="http://blog.allen.com.au/">
    <author>
      <name>Matt Allen</name>
    </author>
    <id>tag:blog.allen.com.au,2007-08-12:9</id>
    <published>2007-08-12T22:56:00Z</published>
    <updated>2007-08-12T22:56:30Z</updated>
    <link href="http://feeds.feedburner.com/~r/RailsInTheCountry/~3/143461650/lighthouse-app-svn-and-you" rel="alternate" type="text/html" />
    <title>Lighthouse App, Svn and you</title>
<content type="html">
            &lt;p&gt;Last week we decided that our ad-hoc issue tracking system wasn’t good enough for the serious development we are doing now.  We tried  Trac, Basecamp and iGTD with email submission. None of them really impressed us for various reasons. Trac was the best of the bunch for bug tracking but never really let us spread our wings.  Basecamp is a great app for what it is meant to do but not the best for bug tracking. I setup iGTD to accept tasks from the boss via email, the problem then was closing the loop, when I was done it was up to me to let the boss know.&lt;/p&gt;


	&lt;p&gt;Enter &lt;a href="http://www.lighthouseapp.com"&gt;Lighthouse&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;What a brilliant piece of software, it’s written by the &lt;a href="http://www.activereload.net/"&gt;Active Reload&lt;/a&gt; guys who also wrote &lt;a href="http://mephistoblog.com/"&gt;Mephisto&lt;/a&gt; the blogging software this blog is written in. The brilliance of this software comes of it’s simplicity. It does the job I want it to do almost perfectly.&lt;/p&gt;


	&lt;p&gt;It doesn’t make any assumptions about things like Bug Priority and such, however it does have a standard tagging system and you can save searches into “bins” that are available on the bug searching screen, we have settled on, low,normal,high,highest and &lt;span class="caps"&gt;URGENT&lt;/span&gt;. See below.&lt;/p&gt;


	&lt;p&gt;&lt;a href="http://blog.allen.com.au/assets/2007/8/12/lighthouse-rgb.jpg"&gt;&lt;img src="http://blog.allen.com.au/assets/2007/8/12/lighthouse-rgb_thumb.jpg"&gt;&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;I also implemented the &lt;span class="caps"&gt;SVN&lt;/span&gt; integration, the &lt;span class="caps"&gt;FAQ&lt;/span&gt; can be found &lt;a href="http://lighthouseapp.com/help/setting-up-subversion-integration"&gt;here&lt;/a&gt; Now all I do is put a proper comment in the &lt;span class="caps"&gt;SVN&lt;/span&gt; commit that explains the changes and has the lighthouse syntax in it, an example might be “Changes to log form [#15 state:resolved]”. A commit with that comment in it closes the ticket as well, brilliant.&lt;/p&gt;


	&lt;p&gt;So the aim of this post was really just to send props to Rick and the team at Active Reload, it’s great to see an app by the geeks, for the geeks.&lt;/p&gt;
          </content>  <feedburner:origLink>http://blog.allen.com.au/2007/8/12/lighthouse-app-svn-and-you</feedburner:origLink></entry>
  <entry xml:base="http://blog.allen.com.au/">
    <author>
      <name>Matt Allen</name>
    </author>
    <id>tag:blog.allen.com.au,2007-08-10:8</id>
    <published>2007-08-10T08:28:00Z</published>
    <updated>2007-08-10T08:28:35Z</updated>
    <link href="http://feeds.feedburner.com/~r/RailsInTheCountry/~3/142665529/the-caching-gap-tm-mixin-it-with-cron" rel="alternate" type="text/html" />
    <title>The Caching Gap(tm) ... mixin' it with Cron</title>
<content type="html">
            &lt;p&gt;So the plugin i mentioned in my &lt;a href="http://blog.allen.com.au/2007/8/8/the-caching-gap-tm"&gt;last post&lt;/a&gt; is alive and kicking, it has totally solved the problem we were having.&lt;/p&gt;


	&lt;p&gt;A new problem arose today, now that the pages are begin re-cached as part of the sweeping process, it was taking ages. Ages being up to 60 seconds to re-cache them all, also ages with the boss sitting there waiting for thing to happen. Not so good from a Usability perspective.&lt;/p&gt;


	&lt;p&gt;Again, I was throwing the idea of using &lt;span class="caps"&gt;DRB&lt;/span&gt; around on &lt;span class="caps"&gt;IRC&lt;/span&gt; and &lt;a href="http://smartbomb.com.au/"&gt;Lachie Cox&lt;/a&gt; suggested good old cron. We are already using cron to expire the front page on the hour to keep the info fresh so I thought i’d expand it a little.&lt;/p&gt;


	&lt;p&gt;I created a new model called CacheQueue that had two columns, cache_type and cache_data. The two types of cache I hose now are the regular esxpressions that get handed into an expire_fragment method, or the urls that get the special param handed in and get re-cached up.&lt;/p&gt;


	&lt;p&gt;The sweeper now looks like this&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&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;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="c"&gt;## The  Sweeper&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;TournamentSweeper&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActionController&lt;/span&gt;::&lt;span class="co"&gt;Caching&lt;/span&gt;::&lt;span class="co"&gt;Sweeper&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  observe ...models...&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;after_save&lt;/span&gt;(record)&lt;tt&gt;
&lt;/tt&gt;      ...  &lt;tt&gt;
&lt;/tt&gt;      &lt;span class="co"&gt;CacheQueue&lt;/span&gt;.create({&lt;span class="sy"&gt;:cache_type&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;url&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="sy"&gt;:cache_data&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;/?special_param_to_hose_cache=1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;})&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="co"&gt;CacheQueue&lt;/span&gt;.create({&lt;span class="sy"&gt;:cache_type&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;regex&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="sy"&gt;:cache_data&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;tournaments/&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="dl"&gt;#{&lt;/span&gt;tournament.id&lt;span class="dl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt;-*&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;})&lt;tt&gt;
&lt;/tt&gt;      ...&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;So we’re creating lots of entires in the cache_queues table, all having an expired = 0 column by default.&lt;/p&gt;


	&lt;p&gt;The next step was to write a method that could be called from a rake task that expires the caches, here it is&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&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;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="c"&gt;## The  CacheQueue Class&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;CacheQueue&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActiveRecord&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="pc"&gt;self&lt;/span&gt;.expire&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="r"&gt;begin&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      require &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;action_controller/integration&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="c"&gt;# wite a disc lock&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      lockfile = &lt;span class="co"&gt;RAILS_ROOT&lt;/span&gt; + &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;/log/cache.expire&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="r"&gt;if&lt;/span&gt; &lt;span class="co"&gt;File&lt;/span&gt;.exists? lockfile&lt;tt&gt;
&lt;/tt&gt;        &lt;span class="gv"&gt;$stderr&lt;/span&gt;.puts &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Locked since &lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; + &lt;span class="co"&gt;File&lt;/span&gt;.open(lockfile,&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;r&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;).atime.to_s&lt;tt&gt;
&lt;/tt&gt;        &lt;span class="r"&gt;return&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="co"&gt;FileUtils&lt;/span&gt;.touch lockfile&lt;tt&gt;
&lt;/tt&gt;      caches_to_be_hosed = &lt;span class="co"&gt;CacheQueue&lt;/span&gt;.find(&lt;span class="sy"&gt;:all&lt;/span&gt;,&lt;span class="sy"&gt;:conditions&lt;/span&gt; =&amp;gt; {&lt;span class="sy"&gt;:expired&lt;/span&gt; =&amp;gt; &lt;span class="pc"&gt;false&lt;/span&gt;}, &lt;span class="sy"&gt;:order&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;cache_type DESC, id ASC&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;      hosed_url_caches = []&lt;tt&gt;
&lt;/tt&gt;      hosed_regex_caches = []&lt;tt&gt;
&lt;/tt&gt;      sess = &lt;span class="co"&gt;ActionController&lt;/span&gt;::&lt;span class="co"&gt;Integration&lt;/span&gt;::&lt;span class="co"&gt;Session&lt;/span&gt;.new&lt;tt&gt;
&lt;/tt&gt;      sess.host! &lt;span class="co"&gt;LIVE_HOST&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      c = &lt;span class="co"&gt;ActionController&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;.new &lt;tt&gt;
&lt;/tt&gt;      skipped = &lt;span class="i"&gt;0&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      caches_to_be_hosed.each &lt;span class="r"&gt;do&lt;/span&gt; |cache|&lt;tt&gt;
&lt;/tt&gt;        &lt;span class="c"&gt;# update the db&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;        cache.expired = &lt;span class="pc"&gt;true&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;        cache.save!&lt;tt&gt;
&lt;/tt&gt;        &lt;span class="r"&gt;case&lt;/span&gt; cache.cache_type&lt;tt&gt;
&lt;/tt&gt;          &lt;span class="r"&gt;when&lt;/span&gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;url&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;            &lt;span class="r"&gt;if&lt;/span&gt; hosed_url_caches.include? cache.cache_data&lt;tt&gt;
&lt;/tt&gt;              skipped += &lt;span class="i"&gt;1&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;              &lt;span class="r"&gt;next&lt;/span&gt; &lt;tt&gt;
&lt;/tt&gt;            &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;            sess.get cache.cache_data&lt;tt&gt;
&lt;/tt&gt;            hosed_url_caches &amp;lt;&amp;lt; cache.cache_data&lt;tt&gt;
&lt;/tt&gt;          &lt;span class="r"&gt;when&lt;/span&gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;regex&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;            &lt;span class="r"&gt;if&lt;/span&gt; hosed_regex_caches.include? cache.cache_data&lt;tt&gt;
&lt;/tt&gt;              skipped += &lt;span class="i"&gt;1&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;              &lt;span class="r"&gt;next&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;            &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;            c.expire_fragment(&lt;span class="co"&gt;Regexp&lt;/span&gt;.new(cache.cache_data))&lt;tt&gt;
&lt;/tt&gt;            hosed_regex_caches &amp;lt;&amp;lt; cache.cache_data&lt;tt&gt;
&lt;/tt&gt;        &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="co"&gt;FileUtils&lt;/span&gt;.rm lockfile&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="r"&gt;rescue&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="co"&gt;FileUtils&lt;/span&gt;.rm lockfile&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="gv"&gt;$stderr&lt;/span&gt;.puts ((hosed_regex_caches.size + hosed_url_caches.size).to_s + &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt; caches removed&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;) &lt;span class="r"&gt;if&lt;/span&gt; (hosed_regex_caches.size + hosed_url_caches.size &amp;gt; &lt;span class="i"&gt;0&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="gv"&gt;$stderr&lt;/span&gt;.puts skipped.to_s + &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt; caches skipped&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="r"&gt;if&lt;/span&gt; skipped &amp;gt; &lt;span class="i"&gt;0&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;

&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&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="code"&gt;&lt;pre&gt;&lt;span class="c"&gt;## The  Rake task&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;namespace &lt;span class="sy"&gt;:cache&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  task &lt;span class="sy"&gt;:expire&lt;/span&gt; =&amp;gt;  &lt;span class="sy"&gt;:environment&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="co"&gt;CacheQueue&lt;/span&gt;.expire&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;This write out to $stderr, when it actually does something it send me an email. It also implements locking so it doesn’t run over itself.&lt;/p&gt;


	&lt;p&gt;One of the cool parts of this is that it’ll actually skip over a cache if it already hosed it, so if in the space of the 4 minutes when it was run last the boss had edited the same article twice, which would schedule the re-caching of the front page and other busy pages, this method would only do each one once, your machine will thank you.&lt;/p&gt;
          </content>  <feedburner:origLink>http://blog.allen.com.au/2007/8/10/the-caching-gap-tm-mixin-it-with-cron</feedburner:origLink></entry>
  <entry xml:base="http://blog.allen.com.au/">
    <author>
      <name>Matt Allen</name>
    </author>
    <id>tag:blog.allen.com.au,2007-08-08:7</id>
    <published>2007-08-08T07:25:00Z</published>
    <updated>2007-08-08T09:44:02Z</updated>
    <link href="http://feeds.feedburner.com/~r/RailsInTheCountry/~3/141892155/the-caching-gap-tm" rel="alternate" type="text/html" />
    <title>The Caching Gap(tm)</title>
<content type="html">
            &lt;p&gt;So &lt;a href="http://blog.allen.com.au/2007/8/7/first-post"&gt;my talk&lt;/a&gt; at the July RoRo meetup was mostly about caching rails apps, and how &lt;a href="http://iseekgolf.com"&gt;we&lt;/a&gt; go about it. The main part of our site is the tournament sweeper that monitors everything that can belong to a tournament, articles, scores, courses etc. It also removes some of the busiest pages on the site, namely the front page and the &lt;a href="http://www.iseekgolf.com/newsandtournaments/"&gt;News and Tournaments&lt;/a&gt; page.&lt;/p&gt;


	&lt;p&gt;This has worked all well and good, that was until we got huge amounts of traffic due to our coverage of the &lt;a href="http://www.iseekgolf.com/tournaments/621-bridgestone-invitational-2007"&gt;&lt;span class="caps"&gt;US PGA&lt;/span&gt; Bridgestone Invitational&lt;/a&gt;, the boss and the photographer where over there taking photos and video, most of the articles on that tourney have 15+ images on them.&lt;/p&gt;


	&lt;p&gt;What happened next was pretty inevitable, it was always going to happen just didn’t quite know when. I call it:&lt;/p&gt;


	&lt;p&gt;&lt;strong&gt;The Caching Gap&lt;/strong&gt;&lt;/p&gt;


	&lt;p&gt;It’s the time between when you expire a cache and the next time its written out, &lt;strong&gt;The Caching Gap&lt;/strong&gt; is usually closed up by the next poor bastard to come along and request the page, he’ll be sitting there twiddling his thumbs while the DB gets spanked and the cache rendered. So, what happens if that page takes, say 2-4 seconds to load and that page is getting &amp;gt;200 requests a second? I’ll tell you what happens, all your mongrels go “ok, no cache file present, better spank the DB and write one out”. Once this happens as many times as mongrels you have, things get messy. Enter my newest, and first, plugin, &lt;em&gt;Cache Fixer&lt;/em&gt;.&lt;/p&gt;


	&lt;p&gt;The plugin itself only really does one minor thing, it allows you to hand in a &lt;em&gt;force&lt;/em&gt; attribute to the cache method. At present, the cache method looks for the existence of the fragment you’re trying to cache and if it exists, just uses it, if not it’ll render and cache a new one. The changes are backwards compatible as the new force argument is false by default. Here’s an example:&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&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;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="c"&gt;## The Controller&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;FrontpageController&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ApplicationController&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;   &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;index&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;       &lt;span class="iv"&gt;@expire&lt;/span&gt; = params[&lt;span class="sy"&gt;:special_param_to_hose_cache&lt;/span&gt;] == &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; ? &lt;span class="pc"&gt;true&lt;/span&gt; : &lt;span class="pc"&gt;false&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;       &lt;span class="r"&gt;if&lt;/span&gt; !read_fragment(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;frontpage&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;) || &lt;span class="iv"&gt;@expire&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;          ... heavy lifiting goes here&lt;tt&gt;
&lt;/tt&gt;       &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;   &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;&amp;nbsp;&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&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="code"&gt;&lt;pre&gt;&lt;span class="c"&gt;## The View&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&amp;lt;% cache(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;frontpage&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; , &lt;span class="iv"&gt;@expire&lt;/span&gt;) &lt;span class="r"&gt;do&lt;/span&gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;&lt;tt&gt;
&lt;/tt&gt;   ... render page here ...&lt;tt&gt;
&lt;/tt&gt;&amp;lt;% end %&lt;/span&gt;&lt;span class="dl"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;The @expire variable get set in the controller and used in the view, thus allowing a single call to the method to re-render the cache. This solved half the problem. The other half is sweeping. I first tackled this in a very punk, quick-and-dirty way.&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&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="code"&gt;&lt;pre&gt;&lt;span class="c"&gt;## The Quick and Dirty Sweeper&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;TournamentSweeper&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActionController&lt;/span&gt;::&lt;span class="co"&gt;Caching&lt;/span&gt;::&lt;span class="co"&gt;Sweeper&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  observe ...models...&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;after_save&lt;/span&gt;(record)&lt;tt&gt;
&lt;/tt&gt;    system(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;wget &lt;/span&gt;&lt;span class="il"&gt;&lt;span class="dl"&gt;#{&lt;/span&gt;&lt;span class="co"&gt;LIVE_URL&lt;/span&gt;&lt;span class="dl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt;?special_param_to_hose_cache=1 -O /dev/null &amp;amp;&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;This worked quite well, &lt;span class="caps"&gt;LIVE&lt;/span&gt;_URL is a constant that is set in each rails environment and this was even quite fast due the the &amp; sending the process into the background however not very railsy, not very railsy at all.&lt;/p&gt;


	&lt;p&gt;After a bit of head banging, the good folk in &lt;a href="irc://irc.freenode.net/ror_au"&gt;#ror_au&lt;/a&gt; gave me a few heads up, specifically &lt;a href="http://www.toolmantim.com"&gt;toolmantim&lt;/a&gt; and &lt;a href="http://freelancing-gods.com"&gt;freelancing_god&lt;/a&gt;. This is what I came up with.&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&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="code"&gt;&lt;pre&gt;&lt;span class="c"&gt;## The more Railsy Sweeper&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;TournamentSweeper&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActionController&lt;/span&gt;::&lt;span class="co"&gt;Caching&lt;/span&gt;::&lt;span class="co"&gt;Sweeper&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  observe ...models...&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;after_save&lt;/span&gt;(record)&lt;tt&gt;
&lt;/tt&gt;        require &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;action_controller&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;        require &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;action_controller/integration&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;        sess = &lt;span class="co"&gt;ActionController&lt;/span&gt;::&lt;span class="co"&gt;Integration&lt;/span&gt;::&lt;span class="co"&gt;Session&lt;/span&gt;.new&lt;tt&gt;
&lt;/tt&gt;        sess.host! &lt;span class="co"&gt;LIVE_HOST&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;        sess.get &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;/?special_param_to_hose_cache=1&lt;tt&gt;
&lt;/tt&gt;  end&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;This code was yoinked from the console app on rails, works a treat. Obviously &lt;span class="caps"&gt;LIVE&lt;/span&gt;_HOST is a constant like &lt;span class="caps"&gt;LIVE&lt;/span&gt;_URL is in my app.&lt;/p&gt;


	&lt;p&gt;So that is how I solved my caching problem, I’m throwing it out there to anyone who can think of a better or faster way to do this. What I may do next is flick the re-caching to a &lt;span class="caps"&gt;DRB&lt;/span&gt; server.&lt;/p&gt;


	&lt;p&gt;The plugin is available at &lt;a href="http://rails-oceania.googlecode.com/svn/mattallen/plugins/cache_fix/"&gt;http://rails-oceania.googlecode.com/svn/mattallen/plugins/cache_fix/&lt;/a&gt; it’s not tested beyond me playing around. The plugin includes &lt;a href="http://dev.rubyonrails.org/attachment/ticket/6223/render_component_fix_for_caching_rb.diff"&gt;this patch&lt;/a&gt; too. Solves a nasty cache_sweeper bug when called with the :only param&lt;/p&gt;
          </content>  <feedburner:origLink>http://blog.allen.com.au/2007/8/8/the-caching-gap-tm</feedburner:origLink></entry>
  <entry xml:base="http://blog.allen.com.au/">
    <author>
      <name>Matt Allen</name>
    </author>
    <id>tag:blog.allen.com.au,2007-08-07:2</id>
    <published>2007-08-07T09:13:00Z</published>
    <updated>2007-08-07T09:14:51Z</updated>
    <category term="Rails" />
    <link href="http://feeds.feedburner.com/~r/RailsInTheCountry/~3/141866990/triple-j-twitter" rel="alternate" type="text/html" />
    <title>Triple J Twitter</title>
<content type="html">
            &lt;p&gt;To scratch an itch of mine earlier this week I setup a new twitter account that broadcasts what program is currently on on &lt;a href="http://triplej.net.au"&gt;Triple J&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;The account is at &lt;a href="http://twitter.com/whatsonjjj"&gt;twitter.com/whatsonjjj&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;The code looks like this, it’s obviously specific to &lt;span class="caps"&gt;JJJ&lt;/span&gt; but could be easily modified to any other static &lt;span class="caps"&gt;HTML&lt;/span&gt; source. It gets run from cron on my server at one and 31 minutes past the hour.&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&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;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;require &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;rubygems&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;require &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;hpricot&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;require &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;open-uri&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;now = &lt;span class="co"&gt;Time&lt;/span&gt;.now&lt;tt&gt;
&lt;/tt&gt;hour = now.hour.to_s.rjust(&lt;span class="i"&gt;2&lt;/span&gt;,&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;minute = now.min &amp;lt; &lt;span class="i"&gt;30&lt;/span&gt; ? &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;00&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; : &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;30&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;day = now.strftime(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;%A&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;search =  hour + &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; + minute&lt;tt&gt;
&lt;/tt&gt;url = &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;http://www.abc.net.au/triplej/guide/&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="dl"&gt;#{&lt;/span&gt;day&lt;span class="dl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt;.htm&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;doc = Hpricot(open(url))&lt;tt&gt;
&lt;/tt&gt;doc.search(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;strong&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;).each &lt;span class="r"&gt;do&lt;/span&gt; |ele|&lt;tt&gt;
&lt;/tt&gt;  re = &lt;span class="co"&gt;Regexp&lt;/span&gt;.new(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;^&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; + search+&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;if&lt;/span&gt; ele.inner_text.match(re)&lt;tt&gt;
&lt;/tt&gt;    time = ele.inner_text&lt;tt&gt;
&lt;/tt&gt;    program = ele.parent.parent.parent.search(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;).first.inner_text.gsub(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;,&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;&amp;amp;apos;&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;    puts [day,time,program].join(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt; &lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;    system(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;curl --basic --user 'whatsonjjj:xxxx' &lt;/span&gt;&lt;span class="ch"&gt;\&lt;tt&gt;
&lt;/tt&gt;&lt;/span&gt;&lt;span class="k"&gt;                --data-ascii &lt;/span&gt;&lt;span class="ch"&gt;\&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;status=&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="dl"&gt;#{&lt;/span&gt;[day,time,program].join(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt; &lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)&lt;span class="dl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="ch"&gt;\&amp;quot;&lt;/span&gt;&lt;span class="k"&gt; &lt;/span&gt;&lt;span class="ch"&gt;\&lt;tt&gt;
&lt;/tt&gt;&lt;/span&gt;&lt;span class="k"&gt;               'http://twitter.com/statuses/update.json'&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;Simple walk-though:&lt;/p&gt;


	&lt;p&gt;Grab the time now, figure out if we’re on the hour or the half-hour. Grab today’s guide. Parse it, look for a string tag that starts with HH:[00|30], grab the name of the program, send that over to twitter via their &lt;span class="caps"&gt;API&lt;/span&gt;&lt;/p&gt;


	&lt;p&gt;There is probably a more elegant way to get the name of the program than that, but that  works.&lt;/p&gt;


	&lt;p&gt;I plan to grabbing out some of the other data too, host name and the like.&lt;/p&gt;
          </content>  <feedburner:origLink>http://blog.allen.com.au/2007/8/7/triple-j-twitter</feedburner:origLink></entry>
</feed>
