<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" xml:lang="en-US">
  <id>http://lukeredpath.co.uk/blog</id>
  <link type="text/html" href="http://lukeredpath.co.uk/blog" rel="alternate" />
  
  <title>confessions of a ruby programmer</title>
  <subtitle>lukeredpath.co.uk</subtitle>
  <updated>2009-03-16T00:00:00Z</updated>
  <link rel="self" href="http://feeds.feedburner.com/LukeRedpath" type="application/atom+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/online-accounting-for-freelancers.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/VZAJtYNddW8/online-accounting-for-freelancers.html" rel="alternate" />
    <published>2009-03-16T00:00:00Z</published>
    <updated>2009-03-16T19:18:25Z</updated>
    <title>Online accounting for freelancers</title>
    <content type="html">&lt;p&gt;When I decided to go freelance, I spent a lot of time researching &lt;a href="http://www.contractoruk.com/managed_service_companies/"&gt;the different options available&lt;/a&gt; when it comes to running my own business. The simplest route is to register as a sole trader but this increases your liability and isn&amp;#8217;t the most tax-efficient route (all profit is treated as taxable income). I briefly considered operating through an &lt;a href="http://www.contractoruk.com/managed_service_companies/umbcos.html"&gt;umbrella company&lt;/a&gt; but after some troubles getting up and running with one I decided to bite the bullet and form my own limited company.&lt;/p&gt;
&lt;p&gt;Of course, with this option comes a whole host of administration and responsibilities; you&amp;#8217;re going to need an accountant. You can either find a good accountant and pay them for advice and preparing your end of year returns as and when you need them to or you can pay for a monthly service and have them take care of almost everything for you. Whichever option you choose, it is well worth learning some basic accounting principles and even if you&amp;#8217;re accountants are doing everything for you, I highly recommend keeping your own books too because ultimately as the company director you are liable for any mistakes.&lt;/p&gt;
&lt;h3&gt;Accounting software, the options&lt;/h3&gt;
&lt;p&gt;If you&amp;#8217;re going to do your own books, you&amp;#8217;re probably going to need some kind of software to keep track of everything. You could use a spreadsheet but there are slicker options available. There are a variety of desktop software packages but there isn&amp;#8217;t much available for the Mac (and don&amp;#8217;t want to boot up Windows in a virtual machine just to do my accounts). After some research I came across two online services that cater for the UK: &lt;a href="http://www.freeagentcentral.com/"&gt;FreeAgent&lt;/a&gt; and &lt;a href="http://www.xero.com/"&gt;Xero&lt;/a&gt;. I have used Xero for the past couple of months and have now moved over to FreeAgent for reasons I&amp;#8217;ll outline towards the end of this article.&lt;/p&gt;
&lt;h3&gt;Xero, &amp;#8220;the worlds easiest accounting system&amp;#8221;&lt;/h3&gt;
&lt;p style="float:right;"&gt;&lt;img src="http://dl.getdropbox.com/u/570528/xero.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Xero was the first service I came across; a New Zealand-based company who have recently branched out into the UK. They offer a full accounting solution for £19 + &lt;span class="caps"&gt;VAT&lt;/span&gt; per month.&lt;/p&gt;
&lt;p&gt;Xero has an attractive and intuitive interface and it presents an overview of your company finances in a clear and concise manner. It will allow you to set up your various bank accounts (including any credit card or Paypal accounts) and import statements directly from your bank (it supports the standard &lt;span class="caps"&gt;OFX&lt;/span&gt; and &lt;span class="caps"&gt;QIF&lt;/span&gt; formats). It will generate attractive looking invoices for you and makes it really simple to track and manage your outstanding debtors including the ability to generate &lt;span class="caps"&gt;PDF&lt;/span&gt; statements and email reminders.&lt;/p&gt;
&lt;p&gt;Dealing with expense claims can be a bit convoluted if you&amp;#8217;re a one-man company. Xero&amp;#8217;s expense claims features seem to be aimed more at larger companies that deal with multiple claims from multiple employees, requiring each claim to be itemized and approved which makes the process a bit long-winded.&lt;/p&gt;
&lt;p&gt;Xero&amp;#8217;s killer feature is it&amp;#8217;s transaction reconciliation interface. Xero maintains a strict separation between your &amp;#8220;Xero balance&amp;#8221; and your various bank account balances, which are reconciled when you import a statement. This is all done using a combination of clever transaction matching and an intuitive interface that makes bank reconciliation a piece of cake.&lt;/p&gt;
&lt;h3&gt;FreeAgent, &amp;#8220;online accounting nirvana&amp;#8221;&lt;/h3&gt;
&lt;p style="float:right;"&gt;&lt;img src="http://dl.getdropbox.com/u/570528/freeagent.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;FreeAgent offers an online accountancy solution for a range of different setups, starting from £15 + &lt;span class="caps"&gt;VAT&lt;/span&gt; per month for sole traders up to £25 + &lt;span class="caps"&gt;VAT&lt;/span&gt; per month for limited companies. They also offer a great referral scheme where you receive a permanent 10% discount for each person you refer (and who signs up to a paying account) meaning you can actually get the service for nothing if you refer 10 people. Each person you refer also receives a 10% discount on the monthly subscription.&lt;/p&gt;
&lt;p&gt;FreeAgent is aimed squarely at freelancers and small businesses. Whilst Xero can be a bit intimidating for a newcomer to the world of accountancy, FreeAgent keeps things simple; instead of Accounts Receivable and Accounts Payable, you simply deal with Invoices and Bills. Reports are accessible and clear and concise and like Xero, the user dashboard prevents a great overview of your accounts. It even has an iCal feed of important dates (such as when &lt;span class="caps"&gt;PAYE&lt;/span&gt;, &lt;span class="caps"&gt;VAT&lt;/span&gt; and company returns are due).&lt;/p&gt;
&lt;p&gt;FreeAgent&amp;#8217;s focus on freelancers who operate their own limited companies is one of its strongest selling points; out-of-pocket expenses are dealt with easily, it has basic project management and time-tracking functionality and it does basic payroll for company directors. It even does the calculations for your personal self-assessment tax return.&lt;/p&gt;
&lt;h3&gt;Xero vs FreeAgent, a conclusion&lt;/h3&gt;
&lt;p&gt;Having used Xero for a few months (and finding it a pleasure to use), I ultimately decided to move to FreeAgent for several reasons.&lt;/p&gt;
&lt;p&gt;Firstly, and most importantly, was it&amp;#8217;s support for the flat-rate &lt;span class="caps"&gt;VAT&lt;/span&gt; scheme which Xero doesn&amp;#8217;t currently support. This effectively made Xero&amp;#8217;s reporting functionality useless to me as it wasn&amp;#8217;t able to deal with my &lt;span class="caps"&gt;VAT&lt;/span&gt; and turnover figures correctly. FreeAgent deals with this perfectly, even remembering to exclude revenue outside of the scope of &lt;span class="caps"&gt;VAT&lt;/span&gt; (i.e. for US-based clients).&lt;/p&gt;
&lt;p&gt;Secondly, the features aimed at small, one-man companies such as mine like the directors payroll, self-assessment calculations and simplified expense tracking means my life is made all the more easier, to the point where I&amp;#8217;m actually considering canceling my monthly accountancy service and getting advice on an ad-hoc basis instead (at around a third of the cost).&lt;/p&gt;
&lt;p&gt;One thing missing from both Xero and FreeAgent is multi-currency support although both of them are apparently working on this. However, its not impossible to manage your foreign-currency invoices in either system although it requires doing the currency conversion and handling of exchange rate fluctuations manually.&lt;/p&gt;
&lt;p&gt;Despite having moved away from Xero, my overall experience with it was positive and they were very receptive to my feedback (several members of the Xero team can be found on Twitter). To get the most out of Xero it can be useful to learn about double-entry bookkeeping and some basic accountancy terminology but this isn&amp;#8217;t necessarily a bad thing; in fact I&amp;#8217;d recommend doing this whichever system you use.&lt;/p&gt;
&lt;p&gt;Both offer free 30-day trials and neither lock you into contracts so if you&amp;#8217;re looking for a great, intuitive online accountancy system I&amp;#8217;d recommend giving them both a try. Overall I feel that right now, FreeAgent offers the best combination of features and simplicity for freelancers. If you want to grab that 10% discount, you&amp;#8217;re welcome to &lt;a href="http://www.freeagentcentral.com/?referrer=31ccxhcm"&gt;use my referral code&lt;/a&gt;.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=VZAJtYNddW8:OGSDwtSIlLA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=VZAJtYNddW8:OGSDwtSIlLA:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=VZAJtYNddW8:OGSDwtSIlLA:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=VZAJtYNddW8:OGSDwtSIlLA:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=VZAJtYNddW8:OGSDwtSIlLA:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=VZAJtYNddW8:OGSDwtSIlLA:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=VZAJtYNddW8:OGSDwtSIlLA:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=VZAJtYNddW8:OGSDwtSIlLA:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/VZAJtYNddW8" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/online-accounting-for-freelancers.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/automating-iphone-releases-with-rake.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/kVZGhk72Mlg/automating-iphone-releases-with-rake.html" rel="alternate" />
    <published>2009-03-03T00:00:00Z</published>
    <updated>2009-03-01T18:06:41Z</updated>
    <title>Automating iPhone releases with Rake</title>
    <content type="html">&lt;p&gt;So far for me the release process for &lt;a href="http://lukeredpath.co.uk/squeemote"&gt;Squeemote&lt;/a&gt; has been fairly manual. After checking the final release several times using a development build, then an ad-hoc build, the time would come to create a distribution build. I&amp;#8217;ve done this several times now and each time I&amp;#8217;ve gone through the same tedious steps so I thought it&amp;#8217;s time I wrote myself a &lt;a href="http://rake.rubyforge.org"&gt;Rake&lt;/a&gt; task to automate the process somewhat.&lt;/p&gt;
&lt;p&gt;If you&amp;#8217;re a Ruby/Rails developer you will no doubt have Rake installed. Otherwise, with Ruby installed (which you should have as it ships with Leopard) then you can install the latest version of Rake using the command &lt;code&gt;gem install rake&lt;/code&gt;. If you&amp;#8217;re not familiar with Rake, to use this task, create a file named &lt;code&gt;Rakefile&lt;/code&gt; in the root of your project and add the code below. To run it, simply execute &lt;code&gt;rake release&lt;/code&gt; from a Terminal.&lt;/p&gt;
&lt;p&gt;The Rake task is still in it&amp;#8217;s infancy but I will continue to update the &lt;a href="http://gist.github.com"&gt;gist&lt;/a&gt; as I make further improvements. It performs the following steps:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;Bumps the build number using &lt;a href="http://is.gd/llGp"&gt;agvtool&lt;/a&gt; (see &lt;a href="http://chanson.livejournal.com/125568.html"&gt;this article on using agvtool&lt;/a&gt;)&lt;/li&gt;
	&lt;li&gt;Builds the release using my main target and by default the Development distribution&lt;/li&gt;
	&lt;li&gt;Creates a folder in my saved releases folder and copies the built package (and the dsym file) to the release folder.&lt;/li&gt;
	&lt;li&gt;Compresses the package into a zip file ready for uploading to Apple.&lt;/li&gt;
&lt;/ol&gt;
&lt;script src="http://gist.github.com/72511.js"&gt;&lt;/script&gt;&lt;p&gt;Feel free to modify this to your needs; I encourage you to fork the gist and make your changes available for everybody else.&lt;/p&gt;
&lt;p&gt;The configuration and &lt;span class="caps"&gt;SDK&lt;/span&gt; can be overridden using environment variables so its possible to use this task to build ad-hoc releases too. The main feature currently missing is checking whether or not the build was successful or not before continuing, something I&amp;#8217;ll fix shortly. It also doesn&amp;#8217;t set the agvtool &amp;#8220;marketing version&amp;#8221; (e.g. 1.2), which I generally do manually on my repository master branch when starting work on the next version of my app.&lt;/p&gt;
&lt;p&gt;If anybody else has any nice tips for automating their iPhone development workflow, I&amp;#8217;d like to hear them.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=kVZGhk72Mlg:5GFlNqIRUsw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=kVZGhk72Mlg:5GFlNqIRUsw:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=kVZGhk72Mlg:5GFlNqIRUsw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=kVZGhk72Mlg:5GFlNqIRUsw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=kVZGhk72Mlg:5GFlNqIRUsw:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=kVZGhk72Mlg:5GFlNqIRUsw:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=kVZGhk72Mlg:5GFlNqIRUsw:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=kVZGhk72Mlg:5GFlNqIRUsw:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/kVZGhk72Mlg" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/automating-iphone-releases-with-rake.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/bringing-device-sync-to-squeemote.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/3GfnaWH1aoc/bringing-device-sync-to-squeemote.html" rel="alternate" />
    <published>2009-02-17T16:00:00Z</published>
    <updated>2009-02-17T11:25:34Z</updated>
    <title>Bringing device sync to Squeemote</title>
    <content type="html">&lt;p&gt;Since the first release of Squeemote arrived in the app store in mid-December, over 500 people have bought a copy, which is more than I&amp;#8217;d ever imagined considering it was only originally built to fulfill my own needs (I&amp;#8217;ve wanted to be able to control my Squeezebox like this for years).&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s been a busy couple of months for me, ever since I &lt;a href="http://lukeredpath.co.uk/blog/a-brave-new-world.html"&gt;decided to go it alone&lt;/a&gt; and especially since starting a new client project a few weeks ago, I&amp;#8217;ve not had a lot of time to focus on Squeemote. I&amp;#8217;m starting to pick it up again and I thought I&amp;#8217;d give a small insight into one of the forthcoming and oft-requested features, device synchronization.&lt;/p&gt;
&lt;h3&gt;Getting the interface right&lt;/h3&gt;
&lt;p&gt;Device synching isn&amp;#8217;t something I&amp;#8217;ve used much in the past being the owner of just a single Squeezebox but now I also own a Squeezebox boom its something that has become more interesting to me. I&amp;#8217;m also very aware that a lot of Squeemote owners own numerous devices and use synching frequently.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve always intended to support device synching but I omitted from the first release due to the complexity involved; I believe in releasing early and frequently and I didn&amp;#8217;t want this to hold back the initial release. When I say complexity, I don&amp;#8217;t mean so much on a technical level (the SqueezeCenter &lt;span class="caps"&gt;API&lt;/span&gt; makes it reasonably simple to sync devices) but more in terms of how to create an effective interface for synching devices.&lt;/p&gt;
&lt;p&gt;Having spent some time pondering the issue, I think I&amp;#8217;ve come up with a good first draft of the interface and I want to get it out there for existing Squeemote owners to leave feedback.&lt;/p&gt;
&lt;p style="text-align:center;"&gt;&lt;a href="http://img.skitch.com/20090217-f8n163sctikajpmcfig6t4tdwr.jpg"&gt;&lt;img src="http://img.skitch.com/20090217-f8n163sctikajpmcfig6t4tdwr.jpg" style="width:400px;" alt="" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve reworked the now playing interface slightly to accommodate some extra controls. On the left is the as-yet unimplemented mute control. The Squeemote icon on the right brings up the new Device Selector screen.&lt;/p&gt;
&lt;p style="text-align:center;"&gt;&lt;a href="http://img.skitch.com/20090217-1txdck9w7jdrcq8wt4y82ne69g.jpg"&gt;&lt;img src="http://img.skitch.com/20090217-1txdck9w7jdrcq8wt4y82ne69g.jpg" style="width:400px;" alt="" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s still in it&amp;#8217;s early stages but here&amp;#8217;s how I see it working:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;In it&amp;#8217;s default mode (with sync disabled), it acts much like the existing devices screen. You can choose the active device and hit done, and you&amp;#8217;ll be taken back to the now playing screen for that device. In this mode, you only ever control a single device at a time.&lt;/li&gt;
	&lt;li&gt;With sync mode enabled, one device is marked as active (the device that was active when sync mode was enabled); for the other devices, the &lt;span class="caps"&gt;INACTIVE&lt;/span&gt; button becomes a &lt;span class="caps"&gt;SYNC&lt;/span&gt; button which when pressed, will sync that device with the active device. Control is always done through the active device.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&amp;#8217;m still not sure how this will take into account sync groups but I have a feeling that this might be enough, certainly for an initial release. Other little bonus features from this interface are:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;strong&gt;Quickly&lt;/strong&gt; change device from the now playing screen instead of having go back to library mode and finding the devices tab.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;See&lt;/strong&gt; instantly what is playing on each device.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Power&lt;/strong&gt; control for each device! (another much requested feature and a feature I curse myself for not having implemented sooner)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This will be the focus for the next release (1.2) but there will be a few other extra features thrown in such as browse by Genre.&lt;/p&gt;
&lt;p&gt;Finally, in case you missed it, Squeemote now has &lt;a href="http://lukeredpath.co.uk/squeemote"&gt;an official home&lt;/a&gt;.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=3GfnaWH1aoc:2l1QGCifBaw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=3GfnaWH1aoc:2l1QGCifBaw:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=3GfnaWH1aoc:2l1QGCifBaw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=3GfnaWH1aoc:2l1QGCifBaw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=3GfnaWH1aoc:2l1QGCifBaw:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=3GfnaWH1aoc:2l1QGCifBaw:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=3GfnaWH1aoc:2l1QGCifBaw:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=3GfnaWH1aoc:2l1QGCifBaw:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/3GfnaWH1aoc" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/bringing-device-sync-to-squeemote.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/importing-comments-from-mephisto-into-disqus.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/VqfLZTcif-Y/importing-comments-from-mephisto-into-disqus.html" rel="alternate" />
    <published>2009-02-16T16:00:00Z</published>
    <updated>2009-02-16T11:47:34Z</updated>
    <title>Importing comments from Mephisto into Disqus</title>
    <content type="html">&lt;p&gt;As part of my continuing effort to strip Marley down to the bare-essentials and make it as simple as possible, I&amp;#8217;ve moved my comments over to &lt;a href="http://disqus.com"&gt;Disqus&lt;/a&gt;. This means I don&amp;#8217;t have to worry about having a database for comments, building tools to moderate those comments or handle spam. I simply drop the comments directly into the page using a snippet of Javascript (I&amp;#8217;m actually using &lt;a href="http://github.com/norman/disqus/tree/master"&gt;Norman Clarke&amp;#8217;s Ruby disqus library&lt;/a&gt; to make this even easier).&lt;/p&gt;
&lt;p&gt;To import my old Marley comments into Disqus, I used &lt;a href="http://locomotivation.com"&gt;Locomotivation&amp;#8217;s&lt;/a&gt; handy &lt;a href="http://www.locomotivation.com/blog/2008/12/01/disqus-sinatra-importer.html"&gt;Sinatra Disqus Importer&lt;/a&gt;. It&amp;#8217;s a super-simple Sinatra app that takes an &lt;span class="caps"&gt;RSS&lt;/span&gt; feed of your existing blog posts and comments and uses that to feed the comments into Disqus using it&amp;#8217;s &lt;span class="caps"&gt;API&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;I wanted to do the same for all of my old Mephisto-powered blog comments. Doing this was fairly trivial, it just required a script to take the content from the Mephisto database and spit out an &lt;span class="caps"&gt;RSS&lt;/span&gt; feed that I could use with the Sinatra importer. Having imported a mysql dump from my server into my local MySQL database, I was able to use ActiveRecord and Builder to generate the &lt;span class="caps"&gt;RSS&lt;/span&gt; feed; here&amp;#8217;s the code in case it&amp;#8217;s of any use to anybody. It&amp;#8217;s a bit rough and you might need to tweak it a bit but it does the job.&lt;/p&gt;
&lt;script src="http://gist.github.com/64712.js"&gt;&lt;/script&gt;&lt;noscript&gt;&lt;a href="http://gist.github.com/64712"&gt;View this gist on github&lt;/a&gt;&lt;/noscript&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=VqfLZTcif-Y:VGjKEQS1GqI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=VqfLZTcif-Y:VGjKEQS1GqI:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=VqfLZTcif-Y:VGjKEQS1GqI:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=VqfLZTcif-Y:VGjKEQS1GqI:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=VqfLZTcif-Y:VGjKEQS1GqI:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=VqfLZTcif-Y:VGjKEQS1GqI:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=VqfLZTcif-Y:VGjKEQS1GqI:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=VqfLZTcif-Y:VGjKEQS1GqI:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/VqfLZTcif-Y" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/importing-comments-from-mephisto-into-disqus.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/remote-pair-programming-with-ichat.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/asF89e9GN0A/remote-pair-programming-with-ichat.html" rel="alternate" />
    <published>2009-02-03T15:40:17Z</published>
    <updated>2009-02-17T11:25:34Z</updated>
    <title>Remote pair-programming using iChat</title>
    <content type="html">&lt;p&gt;When I worked at &lt;a href="http://www.reevoo.com"&gt;Reevoo&lt;/a&gt; we tried to live by a mantra of &amp;#8220;pair on all production code&amp;#8221;. Sometimes this wasn&amp;#8217;t practical (there would often be times when there was an odd number of us available) but its a rule we tried to stick to wherever possible.&lt;/p&gt;
&lt;p&gt;Obviously, now I have gone freelance, pairing isn&amp;#8217;t always a luxury I&amp;#8217;ll have &amp;#8211; that&amp;#8217;s fine, I&amp;#8217;m comfortable working on my own &amp;#8211; but I&amp;#8217;ve always felt that some of the best code I&amp;#8217;ve ever written has been code that I have written in a pair. Fortunately, for my latest client, I am able to work with my former Reevoo colleague &lt;a href="http://interblah.net"&gt;James Adam&lt;/a&gt; who some of you may know as the author of the &lt;a href="http://rails-engines.org/"&gt;Rails Engines&lt;/a&gt; plugin and one of the co-organisers of the &lt;a href="http://lrug.org"&gt;London Ruby User Group&lt;/a&gt; and the awesome &lt;a href="http://rubymanor.org/"&gt;RubyManor conference&lt;/a&gt; (don&amp;#8217;t forget to &lt;a href="http://rubymanor.org/videos/"&gt;check out some of the videos&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Coding remotely&lt;/h3&gt;
&lt;p&gt;From the outset, we were both agreed that we would prefer to pair on as much code as possible. The problem would be that we wouldn&amp;#8217;t always be in the same location. We&amp;#8217;ve both taken up membership at &lt;a href="http://the-hub.net/"&gt;The Hub&lt;/a&gt; which gives us a shared space to work once a week or so but the rest of the time we would both be working from home.&lt;/p&gt;
&lt;p&gt;This isn&amp;#8217;t a new problem; in fact it was one that &lt;a href="http://www.floehopper.org/"&gt;some&lt;/a&gt; &lt;a href="http://chrisroos.co.uk/"&gt;of us&lt;/a&gt; at Reevoo &lt;a href="http://blog.floehopper.org/articles/2008/03/13/remote-pair-programming"&gt;have experience with&lt;/a&gt; using a combination of &lt;a href="http://en.wikipedia.org/wiki/GNU_Screen"&gt;screen&lt;/a&gt; and &lt;a href="http://www.vim.org/"&gt;vim&lt;/a&gt;. However, vim isn&amp;#8217;t really my bag and I&amp;#8217;m far more productive using my editor of choice, &lt;a href="http://macromates.org"&gt;Textmate&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.realvnc.com/"&gt;&lt;span class="caps"&gt;VNC&lt;/span&gt;&lt;/a&gt; was an obvious solution but if you&amp;#8217;re both Mac users,  &lt;a href="http://www.apple.com/macosx/features/ichat.html"&gt;iChat&lt;/a&gt; offers a far more integrated solution, offering instant-messaging, voice chat and most importantly, seamless screen-sharing. iChat&amp;#8217;s built-in screen-sharing solution is fast enough to work on one person&amp;#8217;s machine whilst making it easy to switch back to your own machine when necessary. Even better is if you have a dual-screen setup (I run my MacBook connected to a Dell 20&amp;quot; monitor). Using this setup, I can run James&amp;#8217; screen on my Dell, leaving my MacBook free for background tasks like email, twitter etc. It&amp;#8217;s not perfect, there are a few technical niggles; but it has worked well for us so far.&lt;/p&gt;
&lt;h3&gt;Remote pairing tips&lt;/h3&gt;
&lt;p&gt;What has surprised me the most is just how effective this approach has been. I feel like we have been working just as effectively as if we had been sat in the same room together. We get from all of the usual benefits of pairing; the ability to bounce ideas off each-other, catch minor mistakes that the other person has missed, discuss alternative approaches to a problem and tempering another person&amp;#8217;s enthusiasm with some caution (e.g. &amp;#8220;maybe we could do [some cool idea]&amp;#8230;yes that would be good but do we really need to do it now?&amp;#8221;). If I would offer some tips when using this approach, they would be:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;strong&gt;Make sure&lt;/strong&gt; you&amp;#8217;re both ready to start before you get going. Nobody likes to feel like they are being pressured into doing something. Pair-programming all day can be really draining so make sure you&amp;#8217;re both happy to begin.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Don&amp;#8217;t hesitate&lt;/strong&gt; to suggest to your pair that it&amp;#8217;s time for a break. It can be good to stop for 5 minutes every now and again to make a cup of tea, catch up on some emails.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Try to avoid&lt;/strong&gt; unnecessary distractions whilst in full-flow. Try to turn off other IM clients or at least set your status so people know you shouldn&amp;#8217;t be disturbed. Disable growl notifications or other annoying popups (e.g. Twitter clients). There will always be a few minutes downtime here and there which you can use to check these. Trying to do it whilst in the middle of pairing may lead to errors or missing important information and its unfair on your pair if you&amp;#8217;re not paying attention.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Agree on who&lt;/strong&gt; is driving at any given time, and take it in turns, swapping periodically. James and I tend to work using the &amp;#8220;driver writes a test, pair implements test&amp;#8221; approach. We&amp;#8217;ll swap roles frequently but its important to know who is the main driver so you aren&amp;#8217;t always trying to type at the same time or grabbing the mouse when somebody is trying to move in the other direction. This is probably more of an issue with remote pairing than normal pairing (unless you&amp;#8217;re using multiple keyboards/mice!).&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Don&amp;#8217;t forget&lt;/strong&gt; that when you are connected to your pair&amp;#8217;s machine (and it&amp;#8217;s in the foreground), all of your keystrokes will be sent to the remote machine. &lt;code&gt;def my_methodhello mum&lt;/code&gt; probably won&amp;#8217;t pass any tests.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Bonus Tip&lt;/strong&gt;: Quicksilver can be useful for leaving the equivalent of little notes to eachother whilst the other person is away; if you&amp;#8217;re pair has gone to make a tea/popped to the toilet and you need to leave too, just leave them a nice big message on their screen &amp;#8211; bring up Quicksilver, hit Cmd+Period to enter text mode, write your message then hit tab and select &amp;#8220;Large Type&amp;#8221;. They can&amp;#8217;t miss it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&amp;#8217;ve never paired before, or have been put off of pairing because you work remotely, give it a try; you may find you write better code than you ever did before. Does anybody else pair remotely? If so, I&amp;#8217;d love to hear your tips.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=asF89e9GN0A:c_vVikV6p-8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=asF89e9GN0A:c_vVikV6p-8:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=asF89e9GN0A:c_vVikV6p-8:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=asF89e9GN0A:c_vVikV6p-8:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=asF89e9GN0A:c_vVikV6p-8:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=asF89e9GN0A:c_vVikV6p-8:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=asF89e9GN0A:c_vVikV6p-8:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=asF89e9GN0A:c_vVikV6p-8:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/asF89e9GN0A" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/remote-pair-programming-with-ichat.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/squeemote-1-1-update.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/Pp1Y1WSIshI/squeemote-1-1-update.html" rel="alternate" />
    <published>2009-01-23T00:00:00Z</published>
    <updated>2009-02-15T08:37:40Z</updated>
    <title>Squeemote 1.1 now available in the iTunes store</title>
    <content type="html">&lt;p&gt;Squeemote 1.1 was finally approved by Apple this morning and is now &lt;a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=298005260&amp;mt=8"&gt;available in the iTunes store&lt;/a&gt;. This is quite a hefty update with a number of new features, improvements to existing ones and numerous bug and performance fixes. Here&amp;#8217;s the run down of changes:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Added support for browsing Music Services and Internet Radio stations.&lt;/li&gt;
	&lt;li&gt;Improved handling of music service and internet radio streams on the now playing screen, including displaying the correct artist/album/title/artwork for music service tracks.&lt;/li&gt;
	&lt;li&gt;Squeemote will now automatically discover your server.&lt;/li&gt;
	&lt;li&gt;Added support for server authentication and improved the layout of the initial setup screen.&lt;/li&gt;
	&lt;li&gt;Reduced initial startup time.&lt;/li&gt;
	&lt;li&gt;Improved display of album tracks when browsing music library.&lt;/li&gt;
	&lt;li&gt;Artists list now respects &amp;#8220;ignored articles&amp;#8221; server setting when sorting.&lt;/li&gt;
	&lt;li&gt;Added &amp;#8220;clear playlist&amp;#8221; button to current playlist screen.&lt;/li&gt;
	&lt;li&gt;Improved responsiveness of volume slider control.&lt;/li&gt;
	&lt;li&gt;Added &amp;#8220;disable auto-lock&amp;#8221; setting to prevent device from going into standby.&lt;/li&gt;
	&lt;li&gt;Numerous bug-fixes and performance/reliability improvements.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are a few outstanding known issues mainly relating to Squeemote no longer picking up changes from the server after coming out of standby. These will be resolved in 1.2 which is progressing nicely and will hopefully be submitted to Apple in the next week or so.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m also pleased to announce that Squeemote now has &lt;a href="http://lukeredpath.co.uk/squeemote"&gt;it&amp;#8217;s own website&lt;/a&gt;. It&amp;#8217;s not quite finished yet but I believe in releasing as soon as possible. Look out for a Help/&lt;span class="caps"&gt;FAQ&lt;/span&gt; page over the weekend.&lt;/p&gt;
&lt;p&gt;On a technical note, I did a fairly significant piece of refactoring to the underlying code that retrieves the remote data and displays a &amp;#8220;Loading&amp;#8230;&amp;#8221; view whilst this is happening. I hope to write a blog post about the methods I used to accomplish this and will be releasing the generic code behind this under the &lt;span class="caps"&gt;MIT&lt;/span&gt; license.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=Pp1Y1WSIshI:tDXoK2edkqo:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=Pp1Y1WSIshI:tDXoK2edkqo:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=Pp1Y1WSIshI:tDXoK2edkqo:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=Pp1Y1WSIshI:tDXoK2edkqo:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=Pp1Y1WSIshI:tDXoK2edkqo:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=Pp1Y1WSIshI:tDXoK2edkqo:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=Pp1Y1WSIshI:tDXoK2edkqo:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=Pp1Y1WSIshI:tDXoK2edkqo:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/Pp1Y1WSIshI" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/squeemote-1-1-update.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/squeemote-for-iphone-ready-for-sale.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/33BhSjQvI-8/squeemote-for-iphone-ready-for-sale.html" rel="alternate" />
    <published>2008-12-19T21:47:07Z</published>
    <updated>2009-03-02T09:28:06Z</updated>
    <title>Squeemote, the Squeezebox iPhone remote, ready for sale</title>
    <content type="html">&lt;div class="notice"&gt;&lt;strong&gt;Update&lt;/strong&gt;: You can find out more information about Squeemote on &lt;a href="http://lukeredpath.co.uk/squeemote"&gt;the official website&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;After some wrangling back and forth, I&amp;#8217;m happy to announce that my first iPhone application, Squeemote (formerly iSqueeze) is now &lt;a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=298005260&amp;amp;mt=8"&gt;available on the iTunes application store&lt;/a&gt;.&lt;/p&gt;
&lt;p style="float:right;"&gt;&lt;img src="http://farm4.static.flickr.com/3193/3120384208_d0211a396c.jpg?v=0" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Squeemote for iPhone and iPod Touch is a remote control application for the &lt;a href="http://slimdevices.com"&gt;SlimDevices Squeezebox&amp;trade;&lt;/a&gt; and similar devices such as the Duet and Boom. This first release is aimed at people who mainly use their own music library rather than streaming services (support of which is currently limited). I intend to add browsing support for streaming services in a later release but my main focus for this initial release was on quality over quantity: I didn&amp;#8217;t (and do not) want to cram it with features. I focussed on making this the best possible experience for browsing your SqueezeCentre library and controlling your device.&lt;/p&gt;
&lt;p&gt;I already have some ideas on further improving the user experience for music browsing and playlist management that will appear in a future release. Another area which is currently quite basic is device management; Squeemote supports multiple devices and allows you to choose which device to use and set a default device but there is currently no support for grouping devices for synchronous playback. This, plus browsing support for streaming services will be my priority in the next major release.&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s been an interesting experience writing my first ever iPhone (or Cocoa) application and getting it submitted to the iTunes store but I&amp;#8217;ll save the technical notes for a separate post.&lt;/p&gt;
&lt;p&gt;If you have any questions or problems, feel free to leave a comment or drop me an email. I have also set up a &lt;a href="http://getsatisfaction.com/lukeredpath/products/lukeredpath_squeemote"&gt;support page on GetSatisfaction&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=298005260&amp;amp;mt=8"&gt;&lt;img src="http://lukeredpath.co.uk/assets/itunesstore.png" title="Download Squeemote from the iTunes Store" alt="Download Squeemote from the iTunes Store" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=33BhSjQvI-8:WrfKAJQ0REc:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=33BhSjQvI-8:WrfKAJQ0REc:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=33BhSjQvI-8:WrfKAJQ0REc:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=33BhSjQvI-8:WrfKAJQ0REc:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=33BhSjQvI-8:WrfKAJQ0REc:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=33BhSjQvI-8:WrfKAJQ0REc:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=33BhSjQvI-8:WrfKAJQ0REc:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=33BhSjQvI-8:WrfKAJQ0REc:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/33BhSjQvI-8" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/squeemote-for-iphone-ready-for-sale.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/quick-and-easy-hosted-websites-with-github.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/QEdasEhuLBQ/quick-and-easy-hosted-websites-with-github.html" rel="alternate" />
    <published>2008-12-18T21:47:07Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>Quick and easy hosted websites with GitHub</title>
    <content type="html">&lt;p&gt;&lt;a href="http://twitter.com/defunkt"&gt;Chris Wanstrath&lt;/a&gt; from GitHub recently tweeted about &lt;a href="http://defunkt.github.com/"&gt;his new website&lt;/a&gt;. Whilst trying to load it earlier today I accidentally misspelt his username, revealing a GitHub feature that I hadn&amp;#8217;t come across &amp;#8211; GitHub pages.&lt;/p&gt;
&lt;p&gt;Simply go to yourusername.github.com and you&amp;#8217;ll be presented with a friendly error page explaining how to set up your own github.com sub-domain website. It&amp;#8217;s fairly straightforward &amp;#8211; create a repository named yourusername.github.com, create a local master branch and push it to GitHub. Within ten minutes or so, your new site will be up and running. Very cool. &lt;a href="http://lukeredpath.github.com"&gt;Here&amp;#8217;s mine&lt;/a&gt; (yes, fairly pointless I know). And &lt;a href="http://eoghanmccabe.github.com/"&gt;here is another&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; It seems that I just beat the guys at GitHub to it with &lt;a href="http://github.com/blog/272-github-pages"&gt;their official announcement&lt;/a&gt;.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=QEdasEhuLBQ:O8RPB45tufs:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=QEdasEhuLBQ:O8RPB45tufs:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=QEdasEhuLBQ:O8RPB45tufs:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=QEdasEhuLBQ:O8RPB45tufs:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=QEdasEhuLBQ:O8RPB45tufs:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=QEdasEhuLBQ:O8RPB45tufs:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=QEdasEhuLBQ:O8RPB45tufs:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=QEdasEhuLBQ:O8RPB45tufs:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/QEdasEhuLBQ" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/quick-and-easy-hosted-websites-with-github.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/a-brave-new-world.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/pWy-WPlY4D8/a-brave-new-world.html" rel="alternate" />
    <published>2008-12-04T15:10:53Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>A Brave New World</title>
    <content type="html">&lt;p&gt;I&amp;#8217;m writing this from the depths of the London Underground system, on a Northern Line train out of the city. I&amp;#8217;ve just come from several meetings regarding some forthcoming opportunities. This is the beginning of the next step of my career as a Ruby programmer.&lt;/p&gt;
&lt;p&gt;Last Friday was my last day at &lt;a href="http://www.reevoo.com"&gt;Reevoo&lt;/a&gt;. I had been contemplating leaving for about a month when some unexpected changes at Reevoo forced my hand. In many ways I had become tired of working on the same thing week in, week out and I craved new challenges. It is with some regret however that I leave what is in my opinion (and others) the best Ruby/Rails team in the UK. I have learnt a lot over the last 18 months and much of that is due to the sheer breadth and diversity of knowledge across that team as well as everybody else at Reevoo. I have learnt how to be a better team player, I have come to appreciate pair-programming more than I ever did and I have faced the real day to day challenges of being part of an &amp;#8220;agile&amp;#8221; team.&lt;/p&gt;
&lt;p&gt;As of this week, I officially enter the world of Ruby contracting. Inspired by the likes of Obie Fernandez (I&amp;#8217;m a big fan of his blog and I love what the guys over at &lt;a href="http://www.hashrocket.com/"&gt;Hashrocket&lt;/a&gt; are doing), &lt;a href="http://blog.jayfields.com/2008/08/be-your-start-up.html"&gt;Jay Fields&lt;/a&gt; (the linked article is what really got me thinking in the first place	) and &lt;a href="http://interblah.net/leaving-reevoo"&gt;James Adam&lt;/a&gt;, (my former Reevoo colleague, who has recently gone &amp;#8220;free-range&amp;#8221;), I have decided to take the plunge and fully invest in building my &amp;#8220;brand&amp;#8221; and jumping head first into the contracting world. I have been working with Rails since almost the beginning and it has taken me nearly four years to get to this point. It&amp;#8217;s a scary prospect, leaving the comfort and safety of a regular monthly income but with hard work there is the prospect of great rewards. I look forward to working with other members of the Ruby and Rails community across a wide range of projects.&lt;/p&gt;
&lt;p&gt;&lt;script type="text/javascript"&gt;
/* &lt;![CDATA[ */
function hivelogic_enkoder(){var kode=
"kode=\"oked\\\"=kode\\\"\\\\'=()i'jn)oe.r(vsre)e'.t'l(siepo.=ddkke\\\\o;\\"+
"\\\\\\kd\\\"=o\\\\e\\\\\\\"\\\\\\\\\\\\\\\\r\\\\hn%gu@kq(jjCius{tk4zx}zo(k"+
"gbn.kBC&amp;bxblg(rbusiotzg@zurzqixFj{gknkiv4zq4bub{z(zbk&amp;boCrbbkT&amp;(&amp;kgjrg&amp;Xko"+
"kyujk|ErKvgxr&amp;ss4ob&amp;bkJ(ub&amp;Dkxgv&amp;ss&amp;otBkggbrD5/(AAC(-~l-xAou6.oC.AuBkqrjt4"+
"zk3m/no7CA/1\\\\8\\\\\\\\\\\\\\\\\\\\\\\\0\\\\~0C1u1kqijg4Gn.x1z/oq7j14unk"+
"xizgoG_.3/q3j3Cu1ko~q.jB4ukkmrntqzjE4unkxizgqGj.4ukkmrnt7z@3-/A-&gt;/@%*{i*u&gt;"+
"lr3+l@n&gt;g?1rhhjokqlw.&gt;~.@,rfhnfgd1FkguDr+h,w6ll0+&gt;?i,f.34f;@{5@&gt;w.lVjuiqr1"+
"FudpFkgu+r,h\\\\f\\\\\\\\\\\\\\\\\\\\\\\\0\\\\n0g0@r\\\\h{\\\\\\\\\\\\\\\""+
"\\\\\\\\;=\\\\'xf'r;io0(i=k;d&lt;.oeeglhnit+;{+=)ocekcda.ChdrAo(e)t3ii-(;&lt;f)c"+
"+01c8=x2=;t+iSgrfno.CramChdr(o)ekcd}=o\\\\ex\\\\\\\\=d\\\"ke;o\\\"\\\\kode"+
"=kode.split('').reverse().join('');\\\"=x''f;roi(0=i;(&lt;okedl.netg-h)1i;=+)"+
"2x{=+okedc.ahAr(t+i)1k+do.ehcratAi(})okedx=(+&lt;iokedl.netg?hokedc.ahAr(toke"+
"dl.netg-h)1':)';\";x='';for(i=0;i&lt;(kode.length-1);i+=2){x+=kode.charAt(i+1"+
")+kode.charAt(i)}kode=x+(i&lt;kode.length?kode.charAt(kode.length-1):'');"
;var i,c,x;while(eval(kode));}hivelogic_enkoder();
/* ]]&gt; */
&lt;/script&gt; if you&amp;#8217;re looking for a Ruby/Rails expert to help out with your project.&lt;/p&gt;
&lt;h3&gt;Blogging, Git style&lt;/h3&gt;
&lt;p&gt;You may have noticed the new design. The main objective of the new design was to move towards a traditional blog-style format; I liked my old design but it certainly placed an emphasis on individual articles, something that I&amp;#8217;ve had less and less time to write, hence the scarcity of posts in the last year. The idea is that with the new design, I feel less pressure to write long article-style entries and therefore blog more frequently. I also feel that now more than ever is an appropriate time for a refresh.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m no longer running Mephisto but am instead using a heavily modified version of &lt;a href="http://github.com/karmi/marley"&gt;Marley&lt;/a&gt;, a lightweight Sinatra app that comes with no admin interface but instead pulls blog entries from a git repository. This means I can write all of my posts offline in my &lt;a href="http://macromates.com"&gt;favorite text editor&lt;/a&gt; in an iterative fashion, stored in a local git repository and pushed to &lt;a href="http://github.com/lukeredpath/blogdata"&gt;github&lt;/a&gt; when I&amp;#8217;m ready to publish (Marley pulls from GitHub by way of a  post-receive hook). Marley is relatively new and was a bit rough around the edges but I found it a great base platform to start hacking away on (as you can see by the frequency of commits to &lt;a href="http://github.com/lukeredpath/marley"&gt;my github fork&lt;/a&gt; over recent days).&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve also abandoned my old nginx/mongrel deployment setup and moved back to Apache running &lt;a href="http://modrails.com"&gt;Passenger&lt;/a&gt; &amp;#8211; the ease with which you can configure and deploy a simple Rack-powered app like Marley (or any &lt;a href="http://sinatra.rubyforge.org"&gt;Sinatra&lt;/a&gt; based app) is astounding. No more messing around with monit configs and restarting of mongrels for me. I&amp;#8217;ve set up some mod_rewrite rules so all of the old blog URLs should 301 redirect to the new URLs.&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s probably a few bugs here and there, and some of the images might be broken until I&amp;#8217;ve had a chance to reupload them. There is currently no syntax highlighting on code samples; this is on my todo list. I&amp;#8217;ll hopefully iron these issues out over the coming days. People who have subscribed to my feed using the FeedBurner &lt;span class="caps"&gt;URL&lt;/span&gt; should not need to change anything; those who were using the direct &lt;span class="caps"&gt;URL&lt;/span&gt; should &lt;a href="http://feeds.feedburner.com/LukeRedpath"&gt;subscribe to the FeedBurner &lt;span class="caps"&gt;URL&lt;/span&gt;&lt;/a&gt; instead.&lt;/p&gt;
&lt;p&gt;Are you a Ruby and/or Rails contractor? How&amp;#8217;s it working out for you?&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=pWy-WPlY4D8:n77gCR1cqUI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=pWy-WPlY4D8:n77gCR1cqUI:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=pWy-WPlY4D8:n77gCR1cqUI:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=pWy-WPlY4D8:n77gCR1cqUI:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=pWy-WPlY4D8:n77gCR1cqUI:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=pWy-WPlY4D8:n77gCR1cqUI:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=pWy-WPlY4D8:n77gCR1cqUI:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=pWy-WPlY4D8:n77gCR1cqUI:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/pWy-WPlY4D8" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/a-brave-new-world.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/iphone-app-store-one-month-on.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/8Yx8TVEXgGE/iphone-app-store-one-month-on.html" rel="alternate" />
    <published>2008-08-12T13:01:00Z</published>
    <updated>2009-04-20T04:58:50Z</updated>
    <title>iPhone App store: one month on</title>
    <content type="html">&lt;p&gt;The iPhone 3G and the app store launched about a month ago now and I think it&amp;#8217;s been somewhat of a mixed bag: there is a plethora of shit on there which makes it hard to spot some of the gems.&lt;/p&gt;
&lt;p&gt;When Apple announced the $99 fee for the developer program, there was an initial feeling that this would help reduce the number of people with little interest in writing original/interesting/quality apps. Unfortunately it hasn&amp;#8217;t; a cursory browse through the app store reveals more tip calculators, fuel/expense trackers, note-taking apps, todo lists, Sudoku, and converters than you could shake a shit-covered stick at. And then there is the really pointless crap: flashlights, novelty apps, &lt;a href="http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=285755136&amp;amp;mt=8"&gt;fucking candles&lt;/a&gt; (and they even have the audacity to charge for this rubbish), not to mention the people who don&amp;#8217;t have a clue what user interface guidelines are all about and what makes a good UI (yes, I&amp;#8217;m talking about you &lt;a href="http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewArtist?id=283449658"&gt;Stevens Creek Software&lt;/a&gt; &amp;#8211; you&amp;#8217;re apps are an abomination).&lt;/p&gt;
&lt;p&gt;That said, there are good apps on there, some of which are of really high quality. Here&amp;#8217;s a few that I use regularly:&lt;/p&gt;
&lt;h3&gt;NetNewsWire&lt;/h3&gt;
&lt;p&gt;I&amp;#8217;ve tried many &lt;span class="caps"&gt;RSS&lt;/span&gt; feed readers on my Mac but I&amp;#8217;ve always come back to NetNewsWire. It&amp;#8217;s the only feed reader that just feels &amp;#8220;right&amp;#8221; to me and the syncing between all of my machines courtesy of it&amp;#8217;s online NewsGator service is probably it&amp;#8217;s killer feature. I&amp;#8217;d tried various web-based readers optimised for the iPhone prior to the the app store launch, including NewsGator, but they always seemed slow and clunky so I was quite happy to see NetNewsWire for the iPhone make it out of the door so early.&lt;/p&gt;
&lt;p&gt;Downloading feeds over an &lt;span class="caps"&gt;EDGE&lt;/span&gt; or &lt;span class="caps"&gt;GPRS&lt;/span&gt; connection can be painful but since upgrading to the iPhone 3G, it has been a pleasure to use, syncing with my online NewsGator account to retrieve a list of feeds and read/unread data.&lt;/p&gt;
&lt;p&gt;A few usability gripes: with a long list of feeds, it can become hard to distinguish between folders and feeds; some kind of visual distinction would be useful. It&amp;#8217;s also not immediately obvious that you can view articles with the built-in web view by tapping on the article heading (there is a separate button to view in Safari). Both minor gripes however and if you need your constant &lt;span class="caps"&gt;RSS&lt;/span&gt; fix, NetNewsWire should be at the top of your &amp;#8220;to buy&amp;#8221; list. &lt;a href="http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=284881860&amp;amp;mt=8"&gt;NetNewsWire App Store link&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Texas Hold&amp;#8217;em Poker (Apple)&lt;/h3&gt;
&lt;p&gt;OK, if you don&amp;#8217;t play poker and your eyes are already glazing over, you might as well skip this one. This app is developed by Apple themselves and it shows. The game is straightforward: compete over a number of venues from your garage though Las Vegas all the way up to Dubai with the stakes and difficulty increasing as you progress.&lt;/p&gt;
&lt;p&gt;The AI isn&amp;#8217;t exactly brilliant and there will be times where the game is just plain frustrating: you&amp;#8217;re heads up and you put your opponent all-in with your KQ suited and he/she calls with 10-2 off-suit, only for the flop to come down 10-10-2. It&amp;#8217;s probably a good thing that this isn&amp;#8217;t for real money, lest you throw your phone out of the nearest window. However, the AI is &amp;#8220;good enough&amp;#8221; for a fun game of poker and it makes a the daily commute slightly less tedious.&lt;/p&gt;
&lt;p&gt;Where this app really shines and makes it so obviously an Apple product is the near-perfect use of the iPhone&amp;#8217;s touchscreen gesture support to provide an intuitive and fun user interface; cards can be folded by flicking them into the middle of the table; chips and be pushed into into the pot to go all-in; checking is as simple as tapping the table. You can also switch between a virtual-table mode (where you can see the players as the look at their cards and place their bets) and a more traditional racetrack mode (familiar to anybody who has played online poker) by simply changing the phone&amp;#8217;s orientation. &lt;a href="http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=284602850&amp;amp;mt=8"&gt;Texas Hold&amp;#8217;em App Store link&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Vicinity&lt;/h3&gt;
&lt;p&gt;One of the first apps that I&amp;#8217;ve played with that takes real advantage of the location-based services available; after working out your location using triangulation or &lt;span class="caps"&gt;GPS&lt;/span&gt;, it provides you with a list of useful locations in your vicinity (do you see what they&amp;#8217;ve done there?): nearby places, cafes, banks, pubs, supermarkets, taxis, hotels and restaurants. Again, this wasn&amp;#8217;t very useful on the original iPhone as the cell-tower triangulation was just too slow for it to be usable but on the 3G its incredibly useful. The wikipedia/flickr links are a nice added bonus, especially if you live in a busy area/large city with many points of interest such as London. &lt;a href="http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=284496131&amp;amp;mt=8"&gt;Vicinity App Store link&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Facebook&lt;/h3&gt;
&lt;p&gt;This native app is a continuation of the good work that the Facebook team have already done with their web app &amp;#8211; for my money one of the best web apps available for the iPhone. The native app doesn&amp;#8217;t offer a whole lot more but it does mean a faster, smoother user experience (e.g., swiping a message in your inbox to reveal a delete button ala Mail) and there is also now a built-in chat option. Unfortunately there still doesn&amp;#8217;t seem to be a way to edit your profile (other than your status, which I update through twitter anyway). &lt;a href="http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=284882215&amp;amp;mt=8"&gt;Facebook App Store link&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Twitterific&lt;/h3&gt;
&lt;p&gt;As well as NetNewsWire, Twitterific was another app that I installed straightaway. It&amp;#8217;s not bad but it doesn&amp;#8217;t match up to it&amp;#8217;s desktop counterpart &amp;#8211; I find the dark interface quite ugly on the iPhone (there is a light interface in the paid-for version although I can&amp;#8217;t find a screenshot of this anywhere &amp;#8211; is it really too much to ask to have this as an option in the free app too?) and scrolling is very slow and clunky. I don&amp;#8217;t mind the ads though so I don&amp;#8217;t really feel compelled to pay for the premium version (the same goes for the desktop version). Despite it&amp;#8217;s lack of polish, I still think it&amp;#8217;s the best Twitter client available (at the moment). &lt;a href="http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=284540316&amp;amp;mt=8"&gt;Twitterific App Store link&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Things&lt;/h3&gt;
&lt;p&gt;I&amp;#8217;ve also grabbed a copy of &lt;a href="http://www.culturedcode.com/things/"&gt;Things&lt;/a&gt;. The interface is very pretty and I&amp;#8217;m sure the app will eventually be great but I still need to motivate myself to use it. One thing I&amp;#8217;ve found lacking is the ability to set alarms/receive reminders for scheduled events although I suspect that is a limitation of the platform itself. You really need to be checking it every morning to get the most out of it (although I suspect that is the idea if you&amp;#8217;re a &lt;span class="caps"&gt;GTD&lt;/span&gt; addict). There is also no syncing with the desktop version which is a deal-breaker for many (not me) but I&amp;#8217;d encourage you to snap it up at the introductory price and wait for syncing which will arrive as a free update. &lt;a href="http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=284971781&amp;amp;mt=8"&gt;Things App Store link&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Speaking of updates&lt;/h3&gt;
&lt;p&gt;Unfortunately, the worst part of the whole experience is the App store itself and the way iTunes and the iPhone sync with eachother. When you plug your iPhone in, any apps you&amp;#8217;ve downloaded via the iPhone app store don&amp;#8217;t seem to sync to iTunes automatically &amp;#8211; instead you need to manually select &amp;#8220;Transfer purchases&amp;#8221;. iTunes also seems to be confused about any updates that I need &amp;#8211; it frequently tells me that I have &amp;#8220;x apps requiring updates&amp;#8221; when in fact there aren&amp;#8217;t any. The iPhone App store app itself seems slow and buggy and on occasion has caused my phone to freeze altogether, requiring a hard reset &amp;#8211; not good.&lt;/p&gt;
&lt;h3&gt;Writing my own apps&lt;/h3&gt;
&lt;p&gt;I&amp;#8217;ve been meaning to learn how to write Objective-C/Cocoa Mac applications for a while now but haven&amp;#8217;t ever gotten around to it. The release of the App store and the iPhone &lt;span class="caps"&gt;SDK&lt;/span&gt; has turned out to be that kick up the backside that I&amp;#8217;ve needed &amp;#8211; I&amp;#8217;ve recently started learning Objective-C/Cocoa and I have a few apps in mind (I&amp;#8217;m already working on a native iPhone interface for the SqueezeCenter software that powers &lt;a href="http://slimdevices.com"&gt;Squeezebox music players&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Given that I have very little knowledge of C and absolutely no &lt;span class="caps"&gt;GUI&lt;/span&gt; app programming experience whatsoever, it&amp;#8217;s been an interesting experience and a change of mindset (coming from a web development background) that I&amp;#8217;m slowing coming to grips with and I&amp;#8217;m sure I&amp;#8217;ll be blogging about it over the next couple of months (&lt;a href="http://fuckingnda.com/"&gt;fucking &lt;span class="caps"&gt;NDA&lt;/span&gt;&lt;/a&gt; not withstanding). As a Ruby programmer by day, there are certainly times when Objective-C makes me want to cry although it has some own elegances (sic) of it&amp;#8217;s own that I&amp;#8217;m starting to appreciate. More to follow&amp;#8230;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=8Yx8TVEXgGE:rT_UlpJN5zQ:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=8Yx8TVEXgGE:rT_UlpJN5zQ:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=8Yx8TVEXgGE:rT_UlpJN5zQ:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=8Yx8TVEXgGE:rT_UlpJN5zQ:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=8Yx8TVEXgGE:rT_UlpJN5zQ:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=8Yx8TVEXgGE:rT_UlpJN5zQ:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=8Yx8TVEXgGE:rT_UlpJN5zQ:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=8Yx8TVEXgGE:rT_UlpJN5zQ:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/8Yx8TVEXgGE" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/iphone-app-store-one-month-on.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/on-iphones-and-user-credentials.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/mC7YokWDa-k/on-iphones-and-user-credentials.html" rel="alternate" />
    <published>2008-08-12T12:31:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>On iPhones and user credentials</title>
    <content type="html">&lt;p&gt;&lt;a href="http://simonwillison.net"&gt;Simon Willison&lt;/a&gt; &lt;a href="http://simonwillison.net/2008/Aug/12/exposure/"&gt;argues&lt;/a&gt; that the iPhone app, Exposure, behaves &amp;#8220;suspiciously&amp;#8221; because it uses an embedded web view for users to log into to their Flickr/Yahoo accounts using OAuth.&lt;/p&gt;
&lt;p&gt;The crux of his argument is that the embedded web view makes it impossible for users to tell if they are logging into the genuine Yahoo OAuth website or a phishing site. &lt;a href="http://simonwillison.net/2008/Aug/12/reviews/"&gt;He cites Pownce&lt;/a&gt; as a better implementation because it opens the authentication page in Safari, which has lead to complaints/negative reviews from users due to the interruption of having to exit the Pownce app, sign in in Safari, then return to the Pownce app.&lt;/p&gt;
&lt;p&gt;Whilst I can see his point about the transparency of using Safari directly, I&amp;#8217;m not convinced that it&amp;#8217;s worth compromising on the end-user experience to attain some kind of technical compliance with a protocol that most users haven&amp;#8217;t heard of nor care about. More to the point, I&amp;#8217;m not convinced this is as big a security issue as is being made out.&lt;/p&gt;
&lt;p&gt;Ultimately, security often boils down to trust. Do you trust Service X with your credentials and personal information? If the answer is no, then you should not use Service X.&lt;/p&gt;
&lt;p&gt;Using an embedded web view in the way that Exposure does isn&amp;#8217;t really any different from a native login UI using iPhone interface widgets &amp;#8211; without the source code to that application you have absolutely now way of knowing what that app is doing with your credentials. Most of the service-based iPhone apps that I use have a native iPhone login screen &amp;#8211; NetNewsWire, Twitter etc. Am I wary of inputting my login details, despite the fact that they could be doing anything with them? No, because I trust the developers of these apps with my credentials (in the same way that I trust them with the same data when I use NewsGator or Twitter.com).&lt;/p&gt;
&lt;p&gt;By passing users over to Safari to login &amp;#8211; despite the transparency &amp;#8211; all they are really doing is shifting the burden of trust on to the login provider and damaging the user experience overall.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=mC7YokWDa-k:MgIV9ZE9PLI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=mC7YokWDa-k:MgIV9ZE9PLI:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=mC7YokWDa-k:MgIV9ZE9PLI:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=mC7YokWDa-k:MgIV9ZE9PLI:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=mC7YokWDa-k:MgIV9ZE9PLI:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=mC7YokWDa-k:MgIV9ZE9PLI:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=mC7YokWDa-k:MgIV9ZE9PLI:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=mC7YokWDa-k:MgIV9ZE9PLI:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/mC7YokWDa-k" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/on-iphones-and-user-credentials.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/announcing-reevoo-labs.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/qrkyBQxemiQ/announcing-reevoo-labs.html" rel="alternate" />
    <published>2008-07-03T16:58:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>Announcing Reevoo Labs</title>
    <content type="html">&lt;p&gt;Here at Reevoo, we&amp;#8217;ve just (literally) gone live with our new open-source website, &lt;a href="http://labs.reevoo.com"&gt;Reevoo Labs&lt;/a&gt;. Our old open-source site is moribund and now redirects to the new site.&lt;/p&gt;
&lt;p&gt;As well as our own personal projects, myself and the others here at Reevoo have developed some useful libraries and Rails plugins many of which we hope to share with the community, including the already well-known Ruby mocking library, &lt;a href="http://labs.reevoo.com/projects/mocha"&gt;Mocha&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The first couple of plugins considered ready for public consumption are already up on the site (sorry about the lack of Subversion repository &amp;#8211; all of our code is in a private repository and we&amp;#8217;re still working on a mirror &amp;#8211; I&amp;#8217;ll try and get some of the projects up on my &lt;a href="http://github.com/lukeredpath"&gt;GitHub project page&lt;/a&gt; next week).&lt;/p&gt;
&lt;p&gt;As well as acting as a place to share our various projects, we&amp;#8217;ll also be maintaining a blog which will contains some interesting articles on some of the problems we&amp;#8217;ve faced and how we solved them as well as general Ruby/Rails articles. Look out for some interesting architecture/infrastructure pieces from our system admins in the next couple of weeks &amp;#8211; don&amp;#8217;t forget to &lt;a href="http://labs.reevoo.com/feed/atom.xml"&gt;subscribe to the Reevoo Labs Atom feed&lt;/a&gt;.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=qrkyBQxemiQ:11at4R-1kGw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=qrkyBQxemiQ:11at4R-1kGw:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=qrkyBQxemiQ:11at4R-1kGw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=qrkyBQxemiQ:11at4R-1kGw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=qrkyBQxemiQ:11at4R-1kGw:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=qrkyBQxemiQ:11at4R-1kGw:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=qrkyBQxemiQ:11at4R-1kGw:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=qrkyBQxemiQ:11at4R-1kGw:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/qrkyBQxemiQ" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/announcing-reevoo-labs.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/engaged.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/eNJ0bKduskg/engaged.html" rel="alternate" />
    <published>2008-06-27T16:03:00Z</published>
    <updated>2009-02-15T10:04:52Z</updated>
    <title>Engaged</title>
    <content type="html">&lt;p&gt;Once again I find myself blogging after a long period away, sorry about that. But I wanted to make a quick public personal announcement.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://farm4.static.flickr.com/3291/3280868833_84fd3c7a93_o.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;On Monday 16th June, after seven and a half years together, Julie accepted my marriage proposal. Thank you Jools.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=eNJ0bKduskg:7szSN7eeIrQ:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=eNJ0bKduskg:7szSN7eeIrQ:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=eNJ0bKduskg:7szSN7eeIrQ:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=eNJ0bKduskg:7szSN7eeIrQ:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=eNJ0bKduskg:7szSN7eeIrQ:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=eNJ0bKduskg:7szSN7eeIrQ:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=eNJ0bKduskg:7szSN7eeIrQ:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=eNJ0bKduskg:7szSN7eeIrQ:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/eNJ0bKduskg" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/engaged.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/for-sale-15-macbook-pro.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/tlaW0AQuDsI/for-sale-15-macbook-pro.html" rel="alternate" />
    <published>2008-02-08T12:44:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>For Sale: 15" MacBook Pro</title>
    <content type="html">&lt;p&gt;Apologies for the long delay between this and the last post &amp;#8212; I&amp;#8217;ve struggled to find the time to post anything of late although I hope to rectify that in the coming months. In the meantime, you&amp;#8217;ll have to make do with a blatant advertisement.&lt;/p&gt;
&lt;p&gt;As the title says, I&amp;#8217;m selling my two year old MacBook Pro to make way for a more recent, but more portable black MacBook. It was my first Mac and it&amp;#8217;s served me well over the years; it&amp;#8217;s still a great machine, I&amp;#8217;m just in the market for something smaller with a little more oomph. Here are the pertinent details:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Rev. A MacBook Pro (March 2006)&lt;/li&gt;
	&lt;li&gt;1.83 Ghz Core Duo processor&lt;/li&gt;
	&lt;li&gt;1.5GB &lt;span class="caps"&gt;RAM&lt;/span&gt;&lt;/li&gt;
	&lt;li&gt;80GB Hard Drive&lt;/li&gt;
	&lt;li&gt;SuperDrive&lt;/li&gt;
	&lt;li&gt;Comes with original box and packaging, accessories and Tiger install CD&lt;/li&gt;
	&lt;li&gt;I can ship it with Leopard pre-installed if you so wish&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It&amp;#8217;s well used and has a few battle scars but nothing serious and is otherwise good condition and in perfect working order. The wireless problems that I mentioned in a previous blog post seem to have gone away since the latest Leopard update (so I guess it was a software issue after all) and there are no other issues with the machine.&lt;/p&gt;
&lt;p&gt;So if you&amp;#8217;re looking to hook yourself up with a great value laptop which is perfect for Ruby/Rails development you can &lt;a href="http://cgi.ebay.co.uk/ws/eBayISAPI.dll?ViewItem&amp;amp;item=140205481385&amp;amp;_trksid=p3907.m32&amp;amp;_trkparms=tab%3DSelling"&gt;bid on the item on my eBay auction&lt;/a&gt; or alternatively, take it straight off of my hands without going through eBay for £650 + shipping costs (£20, Royal Mail special delivery) by dropping me an e-mail &amp;#8211; there is a contact link at the top of the page. If you are based in London, I&amp;#8217;m also happy for you to collect it. See the eBay auction page for full details and some photos and don&amp;#8217;t hesitate to e-mail me with any questions.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=tlaW0AQuDsI:j9Wo5eCk7tw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=tlaW0AQuDsI:j9Wo5eCk7tw:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=tlaW0AQuDsI:j9Wo5eCk7tw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=tlaW0AQuDsI:j9Wo5eCk7tw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=tlaW0AQuDsI:j9Wo5eCk7tw:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=tlaW0AQuDsI:j9Wo5eCk7tw:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=tlaW0AQuDsI:j9Wo5eCk7tw:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=tlaW0AQuDsI:j9Wo5eCk7tw:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/tlaW0AQuDsI" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/for-sale-15-macbook-pro.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/optimising-symbol-to_proc.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/rKautBBfwn4/optimising-symbol-to_proc.html" rel="alternate" />
    <published>2007-11-26T10:10:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>Optimising Symbol#to_proc</title>
    <content type="html">&lt;p&gt;Whilst pairing with &lt;a href="http://po-ru.com"&gt;Paul&lt;/a&gt; the other day I noticed that he preferred not to use Symbol#to_proc; on asking why, he told me it was because of the unnecessary performance hit that Symbol#to_proc imposed.&lt;/p&gt;
&lt;p&gt;Now I&amp;#8217;m not one for premature optimisation, but with an idiom like Symbol#to_proc likely to be used throughout a codebase, performance hits like this add up and as things stand, the Rails implementation of Symbol#to_proc is pretty expensive:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;require 'benchmark'
require 'rubygems'
require 'active_support'&lt;/code&gt;

&lt;code class="ruby"&gt;BIG_ARRAY = ['x'] * 1000000&lt;/code&gt;

&lt;code class="ruby"&gt;Benchmark.bm do |bm|
  bm.report("Standard block") do
    BIG_ARRAY.map { |c| c.upcase }
  end
  
  bm.report("Symbol#to_proc") do
    BIG_ARRAY.map(&amp;amp;:upcase)
  end
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;Output on my 2Ghz quad-core Mac Pro:&lt;/p&gt;
&lt;pre class="shell"&gt;&lt;code class="shell"&gt;	            user     system      total        real
Standard block  0.720000   0.060000   0.780000 (  0.772927)
Symbol#to_proc  3.030000   0.010000   3.040000 (  3.040889)&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;Ouch. That&amp;#8217;s roughly four times slower. &lt;del&gt;-Based on my naive understanding of how Symbol#to_proc was implemented, it figured that the bottleneck was the creation of a new Proc object for every iteration; the proc doesn&amp;#8217;t need to change for each iteration so surely we could just memoize it&lt;/del&gt;-?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: It seems that my initial assumption was incorrect; to_proc is in fact only called once. The real issue here is not the instantiation of a new proc, but the Rails implementation. Rails uses this slightly more complicated implementation in order to support passing of multiple arguments with the method call:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;Proc.new { |*args| args.shift.__send__(self, *args) }&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;This allows you to do a few neat things with multiple elements of a collection like &lt;code&gt;[1, 2, 3].inject(&amp;amp;:+)&lt;/code&gt; but I consider this supporting an edge case at the expense of performance.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve never found myself in need of the functionality provided by Rails&amp;#8217; implementation (I didn&amp;#8217;t even know it was supported) but I do find myself using the &lt;code&gt;obj.map(&amp;amp;:method)&lt;/code&gt; idiom a lot so the following simplified implementation suits me just fine:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;class Symbol
  def to_proc
    proc { |obj| obj.__send__(self) }
  end
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;The performance gain is significant:&lt;/p&gt;
&lt;pre class="shell"&gt;&lt;code class="shell"&gt;user     system      total        real
Symbol#to_proc  0.910000   0.010000   0.920000 (  0.916718)&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;The implementation itself is trivial but I&amp;#8217;ve made it &lt;a href="http://pastie.caboo.se/122010"&gt;available on pastie&lt;/a&gt; &amp;#8211; just drop it into a file somewhere in your Rails lib folder. If I find the time, I will try and package it up as a basic Rails plugin too. It&amp;#8217;s worth bearing in mind that Ruby 1.9&amp;#8217;s implementation will probably support the passing of arguments like the Rails implementation but hopefully it should be much faster.&lt;/p&gt;
&lt;p&gt;The performance of Symbol#to_proc has also been brought up on &lt;a href="http://m.onkey.org/2007/6/30/let-s-start-with-wtf"&gt;Pratik Naik&amp;#8217;s blog&lt;/a&gt; and this &lt;a href="http://dev.rubyonrails.org/ticket/8818"&gt;Rails ticket&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: Some of my original assumptions about the way Ruby invokes to_proc were incorrect and I have updated my article accordingly.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=rKautBBfwn4:X8IaP8jb5uk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=rKautBBfwn4:X8IaP8jb5uk:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=rKautBBfwn4:X8IaP8jb5uk:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=rKautBBfwn4:X8IaP8jb5uk:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=rKautBBfwn4:X8IaP8jb5uk:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=rKautBBfwn4:X8IaP8jb5uk:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=rKautBBfwn4:X8IaP8jb5uk:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=rKautBBfwn4:X8IaP8jb5uk:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/rKautBBfwn4" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/optimising-symbol-to_proc.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/some-thoughts-on-leopard.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/94i5PZHK4nk/some-thoughts-on-leopard.html" rel="alternate" />
    <published>2007-10-29T11:36:00Z</published>
    <updated>2009-02-15T10:04:52Z</updated>
    <title>Some thoughts on Leopard</title>
    <content type="html">&lt;p&gt;In case you missed the memo, Apple unleashed &lt;a href="http://www.apple.com/macosx/"&gt;Leopard&lt;/a&gt; on Friday. I headed down to the Regent Street Apple Store to see if I could snag a copy; unfortunately, by 6pm the queue had reached epic proportions (at least a 45 minute wait) and I promptly left and instead chose to pick up a copy from Brent Cross on Saturday (I feel sorry for anybody who did bother queueing all evening &amp;#8211; the Brent Cross store was no more busy than usual come Saturday afternoon).&lt;/p&gt;
&lt;p&gt;Rather than spending time discussing Leopard&amp;#8217;s much discussed new major features, I thought it would be interesting to point out some of the smaller, minor but useful updates that I have picked up on so far. I will update this list over the next week or two as I discover new things, good and bad:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Installation didn&amp;#8217;t go too smoothly for me; there appears to be a bug in the install process affecting some machines that means that my hard drive wasn&amp;#8217;t showing up in the &amp;#8220;Select a destination&amp;#8221; panel. Fortunately I was able to find out from the Apple discussion forums that after waiting 10 to 15 minutes your available drives &lt;em&gt;will&lt;/em&gt; appear. One cup of tea later, and the install routine was back on track. I opted to perform an &amp;#8220;Erase and Install&amp;#8221; on my MacBook Pro to get rid of the sheer amount of crap that I had accumulated over the last 18 months which went as smoothly as I&amp;#8217;d expected. I am yet to try the upgrade option on my Mac Mini so &lt;span class="caps"&gt;YMMV&lt;/span&gt; here.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;My first impressions were that Leopard seemed faster and given that it was probably hard at work indexing my hard drive for Spotlight this was pretty impressive. My Airport problems in Tiger (the connection would frequently just stop responding or lose packets making it incredibly frustrating to do anything useful) appear to be resolved, at least, when running on mains power. When running on battery I still seem to be suffering from random disconnections although Leopard at least shows that it has lost its connection and reconnects pretty fast. This may be a problem with my router and still requires further investigation on my part.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;The new dock is not as bad as everybody has made out and I have left it at the bottom for now even though I&amp;#8217;m traditionally a dock-on-the-side person. The new Stacks functionality is useful and looks great although the cool &amp;#8220;fan&amp;#8221; effect only works when the dock is on the bottom. I imagine the novelty will wear off quite quickly and I will be back to having my dock on the side soon enough.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;I&amp;#8217;m sure that most Rails developers have been aware for a while now that &lt;a href="http://weblog.rubyonrails.org/2006/8/7/ruby-on-rails-will-ship-with-os-x-10-5-leopard"&gt;Leopard will ship with Rails&lt;/a&gt;. The new Ruby/Rails stack in Leopard is well thought out and organised and comes with Ruby 1.8.6, Rails 1.2.3 and and a host of other useful gems. Rails itself is simply bundled as a gem and is easily updated. A &lt;a href="http://trac.macosforge.org/projects/ruby/wiki/WhatsNewInLeopard"&gt;good overview of the changes to Ruby in Leopard&lt;/a&gt; is available.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;One utility that anybody who frequently uses &lt;span class="caps"&gt;SSH&lt;/span&gt; and public/private keys would not be without is &lt;a href="http://www.sshkeychain.org/"&gt;SSHKeychain&lt;/a&gt; which integrates with Apple Keychain and acts as an &lt;span class="caps"&gt;SSH&lt;/span&gt; agent. In my experience SSHKeychain could be flaky and would sometimes silently crash for no apparent reason; good news then because as of Leopard, &lt;span class="caps"&gt;OSX&lt;/span&gt; maintains its own Keychain-integrated ssh-agent making passphrase-protected keys painless to use out of the box.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;Speaking of which, the new Leopard Terminal finally supports tabs and is much easier to customize thanks to built-in themes. Despite some niggles (like not being able to jump to a specific tab using Cmd+number) I think that it is finally time to say goodbye to &lt;a href="http://iterm.sourceforge.net/"&gt;iTerm&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;Connecting to your mail server over &lt;span class="caps"&gt;SSL&lt;/span&gt; using a self-signed certificate in Mail.app is no longer a pain in the arse. Before, Mail would prompt you that the server certificate was not from a trusted source every time you tried to connect unless you manually dragged the certificate to the desktop and added it to your keychain. In Leopard, Mail finally has an option to &amp;#8220;always trust this certificate&amp;#8221; as well as a number of other fine-grained trust options.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;The new version of Front Row, based on the Apple TV software, seems like a regression to me. The new interface doesn&amp;#8217;t seem as slick and suffers from some annoying bugs like not being able to view album artwork on a shared library and most annoying of all: there doesn&amp;#8217;t seem to be any way to get Front Row to open on a second monitor. In Tiger, Front Row would always open on the primary display &amp;#8211; this was documented behavior and in Leopard this behavior seems to have disappeared. I like to hook my &lt;span class="caps"&gt;MBP&lt;/span&gt; up to my plasma TV but now Front Row will &lt;strong&gt;only&lt;/strong&gt; open on my &lt;span class="caps"&gt;MBP&lt;/span&gt; screen, even if its not the primary display, severely limiting its usefulness.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;On the plus-side, Quicktime now has some useful full-screen options including options to stretch or fill a widescreen display when viewing videos in a 4:3 aspect ratio. Quicktime &lt;em&gt;does&lt;/em&gt; work on a second display and allows you to select which screen it will display on in full-screen mode independently of your primary display settings.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Update 1: Airport Woes&lt;/h3&gt;
&lt;p&gt;When running in battery mode, Airport performance is horrendous, even after installing the latest keychain fix. The signal will go up and down and the transmit rate will jump up and down wildly between 0 and 54 before eventually just disconnecting &amp;#8211; it seems to disconnect every couple of minutes.&lt;/p&gt;
&lt;p&gt;The question is, is this a) a continuation of the connection stability problems I was having in Tiger but manifesting itself with different symptoms (or simply that Leopard is more responsive to Airport connection status changes than Tiger), b) an entirely new problem created by the latest Airport drivers, c) a hardware issue (dodgy Airport cards?) or d) a problem with my router.&lt;/p&gt;
&lt;p&gt;Until I can test out Airport performance on my Mac Mini in Leopard, I can&amp;#8217;t rule out D although the fact that &lt;a href="http://discussions.apple.com/thread.jspa?threadID=1195706&amp;amp;tstart=0"&gt;many other people&lt;/a&gt; are having the same problem seems to suggests this is not the case. Airport performance seems reliable when connected to the mains, so maybe its an issue of power consumption by the Airport card?&lt;/p&gt;
&lt;p&gt;Something else that I just noticed: there appears to be a rather nasty looking, if ultimately harmless, rendering bug when scrolling in Finder when in column mode:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://farm4.static.flickr.com/3217/3281695126_d9b5ae9d19_o.jpg" alt="" /&gt;&lt;/p&gt;
&lt;h3&gt;More Airport updates&lt;/h3&gt;
&lt;p&gt;I&amp;#8217;m beginning to become more and more convinced that the problem with Airport is a power issue. My Airport connection seems to be very stable with no disconnections when running on mains power but in battery mode the disconnections come very frequently. I&amp;#8217;ve also managed to perform the following test with reliable results: plug in the power adapter and wait a few minutes to confirm a stable Airport connection. Now remove the power adapter &amp;#8211; in my observations, about 80% of the time the Airport connection will drop within 10 seconds. This would indicate to me that the problem lies in the Airport card not being able to draw enough power to operate in a stable manner whilst running in battery mode.&lt;/p&gt;
&lt;h3&gt;Yet another update&lt;/h3&gt;
&lt;p&gt;I mentioned earlier that I was having trouble getting Front Row to display on my TV even though it was set as the primary monitor. Last night I gave it another try and et voila; Front Row was displaying on my TV. Further investigation led me to this post on the Apple discussion forums:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;#8220;Not sure if this is related to the bugs you are having, but one I&amp;#8217;ve noticed and reported to apple is this: When you first start front row it appears on your primary display, it also remembers which display it first started on. So, if you exit but don&amp;#8217;t kill the front row process (in activity manager), front row will always display on that initial primary monitor, even if you swap your primary/secondary in sys pref. I haven&amp;#8217;t tried mirroring the displays. &amp;quot; &lt;cite&gt;&lt;a href="http://discussions.apple.com/message.jspa?messageID=5707414#5707414"&gt;cmendill, Apple Discussion Board&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I haven&amp;#8217;t had the change to verify this (yet) but it makes sense &amp;#8211; if exiting Front Row doesn&amp;#8217;t actually kill the process, then Front Row will not pick up changes to your primary display settings until you do. I still consider this to be a bug but at least it&amp;#8217;s an explanation!&lt;/p&gt;
&lt;p&gt;Leopard reviews:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://arstechnica.com/reviews/os/mac-os-x-10-5.ars"&gt;Ars Technica review&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://online.wsj.com/article/SB119326655774870521.html?mod=technology_main_promo_left"&gt;Walt Mossberg&amp;#8217;s Wall Street Journal review&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.nytimes.com/2007/10/25/technology/circuits/25pogue.html?ex=1350964800&amp;amp;en=46e6096639495a7a&amp;amp;ei=5088&amp;amp;partner=rssnyt&amp;amp;emc=rss"&gt;New York Times&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=94i5PZHK4nk:ekHgd23-ssA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=94i5PZHK4nk:ekHgd23-ssA:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=94i5PZHK4nk:ekHgd23-ssA:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=94i5PZHK4nk:ekHgd23-ssA:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=94i5PZHK4nk:ekHgd23-ssA:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=94i5PZHK4nk:ekHgd23-ssA:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=94i5PZHK4nk:ekHgd23-ssA:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=94i5PZHK4nk:ekHgd23-ssA:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/94i5PZHK4nk" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/some-thoughts-on-leopard.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/demeters-revenge.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/T0oPfHH_9RY/demeters-revenge.html" rel="alternate" />
    <published>2007-10-18T14:01:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>Demeter's Revenge</title>
    <content type="html">&lt;p&gt;For those who aren&amp;#8217;t aware, the &lt;a href="http://en.wikipedia.org/wiki/Law_Of_Demeter"&gt;Law of Demeter&lt;/a&gt; &amp;#8211; when applied to object-oriented programs &amp;#8211; is a rule that determines which objects another object can send messages to based around the notion of an object knowing as little about the internal structure of the objects it interacts with.&lt;/p&gt;
&lt;p&gt;It is commonly summarized as &amp;#8220;Only talk to your immediate friends&amp;#8221;. Please read the above Wikipedia article for more information and background.&lt;/p&gt;
&lt;p&gt;This notion, when applied to Rails applications is equally valid however certain Rails practices seem to encourage demeter violations either through convention or &lt;span class="caps"&gt;API&lt;/span&gt; design and this can cause problems in all layers of your application. I am not &lt;a href="http://blog.jayfields.com/2006/05/law-of-demeter-and-forwardable.html"&gt;the first&lt;/a&gt; &lt;a href="http://blog.jayfields.com/2007/05/law-of-demeter-canary-in-coal-mine.html"&gt;to voice&lt;/a&gt; these concerns.&lt;/p&gt;
&lt;p&gt;The issue of Demeter violations in views is a tricky one and &lt;a href="http://www.dcmanges.com/blog/37"&gt;not everybody agrees&lt;/a&gt; with Jay&amp;#8217;s approach to solving violations in the view. Personally, I feel that when it comes to removing Demeter violations in the view, both approaches of adding simple wrapper/delegate methods or larger presenter-based solutions can be useful and choosing which approach to take depends largely on the complexity of the view in question.&lt;/p&gt;
&lt;h3&gt;Dealing with Demeter violations in Rails&lt;/h3&gt;
&lt;p&gt;However, my  main concern with Rails when it comes to Demeter violations are those found in the controller and model. At &lt;a href="http://www.reevoo.com"&gt;Reevoo&lt;/a&gt; we try to write as much of our code using &lt;acronym title="Test Driven Development"&gt;&lt;span class="caps"&gt;TDD&lt;/span&gt;&lt;/acronym&gt; or &lt;acronym title="Behaviour Driven Development"&gt;&lt;span class="caps"&gt;BDD&lt;/span&gt;&lt;/acronym&gt; as possible, making use of mocks and stubs to avoid unnecessary database calls (in both model and controller tests). It&amp;#8217;s the issue of mocking and stubbing where Demeter violations can be particularly problematic as &lt;a href="http://lazyatom.com"&gt;James&lt;/a&gt; and I found on a recent internal greenfield project.&lt;/p&gt;
&lt;p&gt;Our new project was written using the Rails 2.0 pre-release and was written in a &lt;span class="caps"&gt;REST&lt;/span&gt;-ful fashion. When dealing with nested resources we would often have code that looked a little bit like this:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;class WidgetsController &amp;lt; ApplicationController&lt;/code&gt;

&lt;code class="ruby"&gt;  # POST /users/xxx/widgets
  def create
    # where @user was loaded in a before_filter
    @user.widgets.create(params[:widget])
    # and handle the result...
  end&lt;/code&gt;

&lt;code class="ruby"&gt;end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;We&amp;#8217;re using the Rails convention of creating our associated widget object directly off of the User has_many association proxy. No apparent problem here but given that we wrote this test-first, look at the lengths we had to go through to make this work using appropriate mocking/stubbing:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;class WidgetsControllerCreateActionTest &amp;lt; Test::Unit::TestCase
  def setup
    # usual rails controller test setup here
    @user = mock('user')
    User.stubs(:find).returns(@user)
  end&lt;/code&gt;

&lt;code class="ruby"&gt;  def test_should_create_new_widget_for_parent_user_using_posted_widget_params
    widgets_proxy = mock('association proxy')
    @user.stubs(:widgets).returns(widgets_proxy)
    widgets_proxy.expects(:create).with(:name =&amp;gt; 'my funky widget')
    post :create, :widget =&amp;gt; {:name =&amp;gt; 'my funky widget'}
  end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;Because we are violating Demeter by getting a reference to the association proxy and then calling the create method on it all from within our controller, we&amp;#8217;ve had to create a mock assocation proxy and stub the association proxy method on user to return it before we can set the expectation that we really care about (the :create call). It might not seem like a big deal, but we also have to make sure we stub @user.widgets to return something in every one of our tests for the create action otherwise we&amp;#8217;ll find ourselves having problems with :create calls on a NilObject. Now multiply this issue by every single controller that contains a create action and things start to get very tedious.&lt;/p&gt;
&lt;h3&gt;The solution&lt;/h3&gt;
&lt;p&gt;The solution itself is not complicated and simply involves encapsulating the association proxy:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;class User
  has_many :widgets&lt;/code&gt;

&lt;code class="ruby"&gt;  def create_widget(*args)
    widgets.create(*args)
  end
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;Now our tests become much simpler and the intent clearer:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;class WidgetsControllerCreateActionTest &amp;lt; Test::Unit::TestCase
  def setup
    # usual rails controller test setup here
    @user = mock('user')
    User.stubs(:find).returns(@user)
  end&lt;/code&gt;

&lt;code class="ruby"&gt;  def test_should_create_new_widget_for_parent_user_using_posted_widget_params
    @user.expects(:create_widget).with(:name =&amp;gt; 'my funky widget')
    post :create, :widget =&amp;gt; {:name =&amp;gt; 'my funky widget'}
  end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;For our other tests, we only need to stub one method, the :create_widget method.&lt;/p&gt;
&lt;p&gt;Again, whilst this doesn&amp;#8217;t seem like a lot of effort, we now find ourselves having to write small delegate methods on all of our ActiveRecord models; and it&amp;#8217;s not just create &amp;#8211; we also find ourselves writing similar methods for all of our other association proxy methods (delete, update etc&amp;#8230;). This too becomes very tedious, which is why my first thought was to try and automate the creation of these methods. This is where &lt;a href="http://plugins.code.lukeredpath.co.uk/browser/demeters_revenge/trunk"&gt;Demeter&amp;#8217;s Revenge&lt;/a&gt; comes in.&lt;/p&gt;
&lt;p&gt;&amp;#8220;Demeter&amp;#8217;s Revenge&amp;#8221; is a simple extension to ActiveRecord, written as a Rails plugin that creates a collection of Demeter-friendly methods for your has_many and has_and_belongs_to_many associations. It doesn&amp;#8217;t require any special configuration or installation &amp;#8211; simply install the plugin as you would any other Rails plugin and your methods will become available to you. Here&amp;#8217;s a quick overview of some of the methods you get access to and their standard Rails equivalent:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;# given a User that has_many Widgets
user.build_widget(params) # =&amp;gt; user.widgets.build(params)
user.create_widget(params) # =&amp;gt; user.widgets.create(params)
user.number_of_widgets # =&amp;gt; user.widgets.size (or .length)
user.has_widgets? # =&amp;gt; user.widgets.any?
user.has_no_widgets? # =&amp;gt; user.widgets.empty?
user.find_widgets(params) # =&amp;gt; user.widgets.find(params)&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;For more examples, the plugin comes with a suit of &lt;a href="http://rspec.rubyforge.org"&gt;RSpec&lt;/a&gt; &lt;a href="http://plugins.code.lukeredpath.co.uk/browser/demeters_revenge/trunk/examples"&gt;examples&lt;/a&gt;. If you want to take a peek under the hood at the implementation, there is a &lt;a href="http://plugins.code.lukeredpath.co.uk/browser/demeters_revenge/trunk/spec"&gt;full suite of specs&lt;/a&gt;. You can grab the plugin from my Subversion repository:&lt;/p&gt;
&lt;pre class="shell"&gt;&lt;code class="shell"&gt;svn://lukeredpath.co.uk/var/svn/plugins/demeters_revenge/trunk&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;If you&amp;#8217;re writing Rails apps and mocks and stubs have been causing you pain and/or Demeter violations make you cry, then hopefully this plugin will be of use to you. If you don&amp;#8217;t care about Demeter violations and don&amp;#8217;t use mock&amp;#8217;s and stubs then its probably of less interest to you but I hope you give it a try anyway. If you have any feedback, feel free to drop me an e-mail (see the &amp;#8220;correspondence&amp;#8221; link in the site header bar) or leave a comment below.&lt;/p&gt;
&lt;h3&gt;Addendum&lt;/h3&gt;
&lt;p&gt;One of the things I&amp;#8217;m not sure that I made very clear when I first wrote this entry was that this plugin is by no means a silver bullet to end all of your Demeter violation woes, nor are the problems experienced with mocking/stubbing the only reason to avoid violation Demeter violations which I could spend a whole article expounding on.&lt;/p&gt;
&lt;p&gt;It just so happens that the pain felt when mocking/stubbing is symptomatic of Demeter violations in your code which should be enough to set alarm bells ringing. In the comments, Neil mentions that this is symptomatic of a problem with the mocking framework; whether or not you believe this to be true, any efforts to allow your mocks to work in such a way that would allow Demeter violating code to be easily mocked/stubbed, I fear that this would simply be a case of sweeping the problem under a rug and hoping nobody notices.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=T0oPfHH_9RY:RE60cxcSUyE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=T0oPfHH_9RY:RE60cxcSUyE:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=T0oPfHH_9RY:RE60cxcSUyE:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=T0oPfHH_9RY:RE60cxcSUyE:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=T0oPfHH_9RY:RE60cxcSUyE:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=T0oPfHH_9RY:RE60cxcSUyE:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=T0oPfHH_9RY:RE60cxcSUyE:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=T0oPfHH_9RY:RE60cxcSUyE:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/T0oPfHH_9RY" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/demeters-revenge.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/tumbling.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/WkWq72QH3tU/tumbling.html" rel="alternate" />
    <published>2007-09-23T19:51:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>Tumbling</title>
    <content type="html">&lt;p&gt;Since I started blogging again a few months ago, I thought it would be a good idea to set up a tumblelog (courtesy of &lt;a href="http://tumblr.com"&gt;tumblr&lt;/a&gt;) to encourage me to post more frequently without having to spend time writing full-length blog entries.&lt;/p&gt;
&lt;p&gt;In case you missed new navigation bar along the top of this blog (or you are reading this in your newsreader), then you can access &lt;a href="http://tumble.lukeredpath.co.uk"&gt;my tumblelog here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Besides the obligatory tumblelog fare (quotes, chat excerpts and so on) I tend to post any useful, interesting or plain esoteric code snippets that I have written or come across, so please do check it out. You can also &lt;a href="http://feeds.feedburner.com/tumble-lukeredpath"&gt;subscribe to the feed&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here are some recent snippets:&lt;/p&gt;
* &lt;a href="http://tumble.lukeredpath.co.uk/post/12847007"&gt;Timeout on any Ruby method&lt;/a&gt;
* &lt;a href="http://tumble.lukeredpath.co.uk/post/11530172"&gt;More sneaky #to_proc tricks&lt;/a&gt;
&lt;p&gt;On an unrelated note, I did attend RailsConf Europe last week but have yet to finish my write-up. To everybody I got the chance to talk to while I was out in Berlin, it was good catching up and enjoy numerous beers. I have to say thanks to my employers, &lt;a href="http://www.reevoo.com"&gt;Reevoo&lt;/a&gt; for financing the trip. There were some interesting talks and I enjoyed myself overall but unfortunately were also some disappointments &amp;#8211; more on that in my full write-up.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=WkWq72QH3tU:4Z5kjjzLkuI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=WkWq72QH3tU:4Z5kjjzLkuI:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=WkWq72QH3tU:4Z5kjjzLkuI:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=WkWq72QH3tU:4Z5kjjzLkuI:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=WkWq72QH3tU:4Z5kjjzLkuI:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=WkWq72QH3tU:4Z5kjjzLkuI:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=WkWq72QH3tU:4Z5kjjzLkuI:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=WkWq72QH3tU:4Z5kjjzLkuI:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/WkWq72QH3tU" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/tumbling.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/rimuhosting-singing-their-praises.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/IPIOQrPWSuE/rimuhosting-singing-their-praises.html" rel="alternate" />
    <published>2007-09-02T20:51:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>Rimuhosting...singing their praises</title>
    <content type="html">&lt;p&gt;I don&amp;#8217;t usually post this kind of thing on my blog but when somebody experiences a level of customer service that seems to be so lacking these days, one feels the need to shout about it.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve been hosting this blog on RimuHosting for about a year now &amp;#8211; a basic 160MB &lt;span class="caps"&gt;VPS&lt;/span&gt; setup running nginx and one mongrel to power this blog and &lt;a href="http://lighttpd.net"&gt;lighty&lt;/a&gt; on my second IP for &lt;span class="caps"&gt;PHP&lt;/span&gt; apps (like &lt;a href="http://www.haveamint.com/"&gt;Mint Stats&lt;/a&gt; and &lt;a href="http://roundcube.net/"&gt;RoundCube Webmail&lt;/a&gt;. The &lt;span class="caps"&gt;VPS&lt;/span&gt; also acts as a home to my mail and Subversion repositories.&lt;/p&gt;
&lt;p&gt;In all the time I&amp;#8217;ve been with RimuHosting I&amp;#8217;ve experienced good uptime with problems being sorted out quickly and efficiently. I&amp;#8217;ve not had much need to trouble their support team but having just purchased a shiny copy of &lt;a href="http://warehouseapp.com"&gt;Warehouse&lt;/a&gt; I needed some more &lt;span class="caps"&gt;RAM&lt;/span&gt; to handle a second mongrel instance. I submitted a support ticket asking for a quote, indicating that if it should cost less than an extra $10 a month that they should proceed.&lt;/p&gt;
&lt;p&gt;In the time it took me to purchase and download a copy of Warehouse, scp it to my server and untar the thing, my &lt;span class="caps"&gt;VPS&lt;/span&gt; was restarting with its extra memory. All in all, about 5 minutes from the time I submitted my ticket. On a holiday weekend. On a Sunday. That is true customer service.&lt;/p&gt;
&lt;p&gt;So, if you are in the market for a personal geek-friendly &lt;span class="caps"&gt;VPS&lt;/span&gt; at a decent price with great support then consider this an obligatory plug for &lt;a href="http://rimuhosting.com"&gt;RimuHosting&lt;/a&gt;. Tell &amp;#8217;em Luke sent ya!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: Warehouse is now &lt;a href="http://code.lukeredpath.co.uk/"&gt;up and running&lt;/a&gt;.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=IPIOQrPWSuE:Ui1_tTTKHps:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=IPIOQrPWSuE:Ui1_tTTKHps:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=IPIOQrPWSuE:Ui1_tTTKHps:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=IPIOQrPWSuE:Ui1_tTTKHps:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=IPIOQrPWSuE:Ui1_tTTKHps:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=IPIOQrPWSuE:Ui1_tTTKHps:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=IPIOQrPWSuE:Ui1_tTTKHps:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=IPIOQrPWSuE:Ui1_tTTKHps:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/IPIOQrPWSuE" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/rimuhosting-singing-their-praises.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/sending-sms-messages-from-your-rails-application.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/AqkEKGALDnY/sending-sms-messages-from-your-rails-application.html" rel="alternate" />
    <published>2007-08-29T15:46:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>Sending SMS messages from your Rails application</title>
    <content type="html">&lt;p&gt;Something I&amp;#8217;ve seen come up on the Ruby/Rails mailing lists/google groups from time to time is how to send &lt;span class="caps"&gt;SMS&lt;/span&gt; messages from your Rails app. In this article, I&amp;#8217;ll introduce you to my Ruby Clickatell library and how to use it to send &lt;span class="caps"&gt;SMS&lt;/span&gt; messages from your Rails application in no time.&lt;/p&gt;
&lt;p&gt;When it comes to sending an &lt;span class="caps"&gt;SMS&lt;/span&gt; using Ruby there are two approaches you can take. The first &amp;#8211; and more complicated &amp;#8211; is to use your own hardware and something like the &lt;a href="http://raa.ruby-lang.org/project/ruby-sms/"&gt;ruby-sms&lt;/a&gt; library to communicate with the hardware. This set-up is time consuming, a pain to maintain and probably not very scalable.&lt;/p&gt;
&lt;p&gt;The second option is to take the much easier route and use an existing &lt;span class="caps"&gt;SMS&lt;/span&gt; gateway service. There are many &lt;span class="caps"&gt;SMS&lt;/span&gt; gateways out there that offer APIs (ranging from &lt;span class="caps"&gt;HTTP&lt;/span&gt;/&lt;span class="caps"&gt;FTP&lt;/span&gt; based to email and &lt;span class="caps"&gt;COM&lt;/span&gt;-based); one such provider is &lt;a href="http://www.clickatell.com"&gt;Clickatell&lt;/a&gt; who are one of the bigger providers out there with a range of services whose customers include Barclays Bank, the &lt;span class="caps"&gt;BBC&lt;/span&gt; and &lt;span class="caps"&gt;CNN&lt;/span&gt;.&lt;/p&gt;
&lt;h3&gt;Getting started with the Ruby Clickatell gem&lt;/h3&gt;
&lt;p&gt;Before you can start sending &lt;span class="caps"&gt;SMS&lt;/span&gt; messages from your Rails app, you&amp;#8217;ll need a Clickatell account. You can sign up for an account (Clickatell Central &lt;span class="caps"&gt;API&lt;/span&gt;) on their website. Once you have signed up you can log into your account centre and add an &lt;span class="caps"&gt;HTTP&lt;/span&gt; service to your account &amp;#8211; this will give you an &lt;span class="caps"&gt;API&lt;/span&gt; key that you will need to use in your code.&lt;/p&gt;
&lt;p&gt;Once you&amp;#8217;ve signed up, you need to install the Ruby Clickatell gem. I&amp;#8217;ve just released version 0.4 and its fast approaching being stable for production use &amp;#8211; all it needs is some users to test it out a bit more extensively. So install it and give it a go:&lt;/p&gt;
&lt;pre class="shell"&gt;&lt;code class="shell"&gt;$ sudo gem install clickatell&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;The gem also comes with a handy command-line utility, &amp;#8220;sms&amp;#8221;, which can be used to send &lt;span class="caps"&gt;SMS&lt;/span&gt; messages directly from the command line. For more information, simply run:&lt;/p&gt;
&lt;pre class="shell"&gt;&lt;code class="shell"&gt;$ sms --help&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;More information can be found on the &lt;a href="http://clickatell.rubyforge.org"&gt;website&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Integrating with Rails&lt;/h3&gt;
&lt;p&gt;For our basic example application, which will use &lt;span class="caps"&gt;REST&lt;/span&gt;-ful conventions, we will expose a single resource &amp;#8211; sms &amp;#8211; which we can &lt;span class="caps"&gt;POST&lt;/span&gt; to to send an &lt;span class="caps"&gt;SMS&lt;/span&gt; to a specified recipient. First of all, lets create a simple wrapper around the Clickatell &lt;span class="caps"&gt;API&lt;/span&gt; which will act as our &amp;#8220;resource&amp;#8221; that takes a hash containing our username/password/api-key and has an ActiveRecord-style create method.&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;require 'clickatell'&lt;/code&gt;

&lt;code class="ruby"&gt;class SMS
  def initialize(config)
    @config = config
  end
  
  def create(recipient, message_text)
    api.send_message(recipient, message_text)
  end
  
  private
    def api
      @api ||= Clickatell::API.authenticate(
        @config[:api_key],
        @config[:username],
        @config[:password]
      )
    end
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;The above wrapper isn&amp;#8217;t strictly necessary but it helps to keep our controller as &lt;a href="http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model"&gt;skinny&lt;/a&gt; as possible.&lt;/p&gt;
&lt;p&gt;For convenience, we&amp;#8217;ll want to keep our Clickatell credentials in a &lt;span class="caps"&gt;YAML&lt;/span&gt; file in the config folder of our Rails app:&lt;/p&gt;
&lt;pre class="yaml"&gt;&lt;code class="yaml"&gt;# config/clickatell.yml
api_key: abcdefghi123
username: joebloggs
password: secret&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;We&amp;#8217;ll also want to access this config within our Rails app easily:&lt;/p&gt;
&lt;pre class="yaml"&gt;&lt;code class="yaml"&gt;# config/environments/production.rb
CLICKATELL_CONFIG = YAML.load(File.open(File.join(RAILS_ROOT, 'config', 'clickatell.yml')))&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;h3. Bringing it altogether&lt;/p&gt;
&lt;p&gt;Now we&amp;#8217;ll want to configure our routing to expose our &lt;span class="caps"&gt;SMS&lt;/span&gt; resource:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;ActionController::Routing::Routes.draw do |map|
  map.resource :sms
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;Next, we&amp;#8217;ll create our form under app/views/sms/new.rhtml:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;&amp;lt;% form_tag '/sms', :method =&amp;gt; :post do -%&amp;gt;
  &amp;lt;label&amp;gt;Enter the recipients mobile number:&amp;lt;/label&amp;gt;
  &amp;lt;%= text_field_tag "recipient" %&amp;gt;
  &amp;lt;label&amp;gt;Enter your message:&amp;lt;/label&amp;gt;
  &amp;lt;%= text_area_tag "message_text" %&amp;gt;
  &amp;lt;%= submit_tag "Send SMS" %&amp;gt;
&amp;lt;% end %&amp;gt;&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;Finally, all we have to do is create our controller&amp;#8217;s create method to handle the form submission.&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;class SmsController &amp;lt; ApplicationController
  def create
    sms = SMS.new(CLICKATELL_CONFIG)
    sms.create(params[:recipient], params[:message_text])
    flash[:notice] = "Message sent succesfully!"
    redirect_to :back
  rescue Clickatell::API::Error =&amp;gt; e
    flash[:error] = "Clickatell API error: #{e.message}"
    redirect_to :back
  end
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;And thats all there is to it. Of course, in a real application you might want to think about things such as validation of required attributes and message length. I&amp;#8217;ll leave that as an exercise to the reader.&lt;/p&gt;
&lt;p&gt;For more information:&lt;/p&gt;
* &lt;a href="http://clickatell.rubyforge.org"&gt;Ruby Clickatell &lt;span class="caps"&gt;API&lt;/span&gt; Website&lt;/a&gt;
* &lt;a href="http://clickatell.rubyforge.org/rdoc/"&gt;Ruby Clickatell Documentation&lt;/a&gt;
* &lt;a href="http://rubyforge.org/projects/clickatell"&gt;Rubyforge project page&lt;/a&gt;
* &lt;a href="https://www.clickatell.com/products/gateway.php"&gt;Clickatell Gateway information&lt;/a&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=AqkEKGALDnY:Pnjzg59s4vU:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=AqkEKGALDnY:Pnjzg59s4vU:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=AqkEKGALDnY:Pnjzg59s4vU:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=AqkEKGALDnY:Pnjzg59s4vU:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=AqkEKGALDnY:Pnjzg59s4vU:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=AqkEKGALDnY:Pnjzg59s4vU:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=AqkEKGALDnY:Pnjzg59s4vU:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=AqkEKGALDnY:Pnjzg59s4vU:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/AqkEKGALDnY" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/sending-sms-messages-from-your-rails-application.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/breaking-the-silence.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/iJzI5m9JBqI/breaking-the-silence.html" rel="alternate" />
    <published>2007-08-02T10:50:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>Breaking the silence</title>
    <content type="html">&lt;p&gt;I started my last blog entry with the sentence: &amp;#8220;It&amp;#8217;s been a while since I blogged&amp;#8221;. To use the same sentence to begin this entry would be somewhat of an understatement. Its been six months today since my last post.&lt;/p&gt;
&lt;p&gt;There are various reasons for this but the main one would be lack of motivation. Whilst working at Coherent I managed to lose touch with the Ruby/Rails community; I didn&amp;#8217;t make it to any &lt;a href="http://lrug.org"&gt;&lt;span class="caps"&gt;LRUG&lt;/span&gt;&lt;/a&gt; meetings, I wrote precious little code outside of work and my enthusiasm just disappeared. This was partly due to me being the only developer at Coherent and partly down to me just burning out towards the beginning of the year.&lt;/p&gt;
&lt;p&gt;However, things have changed somewhat over the past few months. I have left Coherent and I am now a member of the great team at &lt;a href="http://www.reevoo.com"&gt;Reevoo&lt;/a&gt;. Since starting at Reevoo, I have regained some of my enthusiasm and have now finally found the time to post something here. I even made it to &lt;span class="caps"&gt;LRUG&lt;/span&gt; last month.&lt;/p&gt;
&lt;p&gt;Allow me to give you a brief update on some of the projects that I have been involved in:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;RSpec article part two: unfortunately this has just been far too long coming. RSpec has changed a fair bit (for the better) since that article and is now quite out of date. There are plenty of other great developers out there writing about RSpec as well now. What I intend to do is revisit my original article and update it to reflect the latest version of RSpec. And while there will not be a &amp;#8220;part two&amp;#8221; per se, I do intend to write about RSpec more in the future.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://www.ujs4rails.com"&gt;&lt;span class="caps"&gt;UJS&lt;/span&gt;&lt;/a&gt; &amp;#8211; the &lt;span class="caps"&gt;UJS&lt;/span&gt; project has come to a grinding halt over the past six months for several reasons on both mine and &lt;a href="http://www.danwebb.net"&gt;Dan&amp;#8217;s&lt;/a&gt; part. We are still undecided on where to go with the project at this point but it is unlikely that development will continue on the plugin unless somebody else volunteers to take over the project. Read &lt;a href="http://www.danwebb.net/2007/6/16/the-state-and-future-of-the-ujs-plugin"&gt;this entry on Dan&amp;#8217;s website&lt;/a&gt; for more information.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;Refactoring &lt;span class="caps"&gt;REST&lt;/span&gt; &amp;#8211; the subject of my last blog entry; I still have this code lying around and whilst I haven&amp;#8217;t had much of a chance to explore it further I still intend on developing this, time permitting.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;Rails Plugin Repository &amp;#8211; work on this, too, has unfortunately come to a halt. I still speak to &lt;a href="http://lazyatom.com"&gt;James Adam&lt;/a&gt; who just so happens to be joining us here at Reevoo in the coming weeks. So maybe this will be resurrected, or maybe it won&amp;#8217;t.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So to the future&amp;#8230;things are going great here at Reevoo &amp;#8211; we&amp;#8217;ve just launched &lt;a href="http://www.reevoo.com"&gt;a brand new version of Reevoo&lt;/a&gt;. Be sure to check it out. We have some other exicting developments in the works too.&lt;/p&gt;
&lt;p&gt;Because I don&amp;#8217;t always have the time to post full-length blog posts I set up a &lt;a href="http://en.wikipedia.org/wiki/Tumblelog"&gt;tumbelog&lt;/a&gt; some time ago which I never got around to using (I didn&amp;#8217;t even publicise it). However I plan to start posting to it frequently from now on, so &lt;a href="http://tumble.lukeredpath.co.uk/"&gt;add it to your bookmarks/news reader feeds&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Finally, I will be attending &amp;#8220;RailsConf Europe 2007&amp;#8221;: along with my Reevoo colleagues &lt;a href="http://po-ru.com/"&gt;Paul&lt;/a&gt; and &lt;a href="http://everythingbehind.com/ruby/"&gt;Ben&lt;/a&gt;. I look forward to seeing you all in Berlin.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=iJzI5m9JBqI:RApHSTErLhc:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=iJzI5m9JBqI:RApHSTErLhc:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=iJzI5m9JBqI:RApHSTErLhc:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=iJzI5m9JBqI:RApHSTErLhc:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=iJzI5m9JBqI:RApHSTErLhc:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=iJzI5m9JBqI:RApHSTErLhc:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=iJzI5m9JBqI:RApHSTErLhc:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=iJzI5m9JBqI:RApHSTErLhc:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/iJzI5m9JBqI" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/breaking-the-silence.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/refactoring-rest-searching-for-an-abstraction.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/sa-nKM0Zh_I/refactoring-rest-searching-for-an-abstraction.html" rel="alternate" />
    <published>2007-02-02T22:12:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>Refactoring REST: searching for an abstraction</title>
    <content type="html">&lt;p&gt;It&amp;#8217;s been a while since I blogged. Sorry about that. Needless to say, it&amp;#8217;s been a hectic couple of months. Birthdays, Christmas, New Year, a new job; all found their way towards keeping me from that big shiny text-box in &lt;a href="http://mephistoblog.com"&gt;Mephisto&amp;#8217;s&lt;/a&gt; admin interface. For those still waiting for part two of my RSpec tutorial &amp;#8211; and I know I&amp;#8217;ve said this several times already &amp;#8211; it will be coming; I&amp;#8217;m just not sure when yet. There are some interesting developments afoot in the world of &lt;a href="http://rspec.rubyforge.org"&gt;RSpec&lt;/a&gt; and I&amp;#8217;ll be waiting until these make it into the next major release before tackling the article.&lt;/p&gt;
&lt;p&gt;In this article, I&amp;#8217;d like to talk about &lt;span class="caps"&gt;REST&lt;/span&gt;. The world of &lt;span class="caps"&gt;REST&lt;/span&gt; and resources has been a part of the Rails edge for a while now, since the last RailsConf in fact and whilst skeptical at first, I&amp;#8217;ve really come around to the concept of RESTful resources. Whilst I still feel that it is not always suitable I&amp;#8217;ve found that the best approach is combination of RESTful controllers and &lt;span class="caps"&gt;RPC&lt;/span&gt;-style controllers where necessary. One of the great things about the RESTful approach is that it really brings the idea of good object-oriented design &amp;#8211; specifically the idea of objects having a clear focus on a particular task &amp;#8211; to Rails controllers. The downside is that normalization of &lt;span class="caps"&gt;REST&lt;/span&gt;-style controllers has led to, in my experience, a lot of repetitive code. And as every good programmer knows, duplication should be hunted down and destroyed whenever possible.&lt;/p&gt;
&lt;h3&gt;The wonderful world of &lt;span class="caps"&gt;REST&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;The RESTful resources concept focusses on seven core actions: index, show, new, create, edit, update and destroy. These actions expose four core behaviours of your ActiveRecord objects through some of the various verbs available in the &lt;span class="caps"&gt;HTTP&lt;/span&gt; spec: retrieval (&lt;span class="caps"&gt;GET&lt;/span&gt;), creation (&lt;span class="caps"&gt;POST&lt;/span&gt;), updating (&lt;span class="caps"&gt;PUT&lt;/span&gt;) and deletion (&lt;span class="caps"&gt;DELETE&lt;/span&gt;). If you&amp;#8217;ve been working with RESTful resources for any length of time you will find that the code for each of these actions tends to look very similar largely due to the use of convention over configuration that Rails favours heavily.&lt;/p&gt;
&lt;p&gt;Whilst looking at refactoring portions of our company intranet, it struck me that such duplication was rife and I set about eliminating it. Ever mindful that the best abstractions are extracted (just like Rails was), all of the ideas and code that follows have been extracted directly from our intranet application.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m not the only person who has recognised the duplication that tends to result from taking the RESTful approach and consequently some solutions for making development of RESTful applications exist already in the form of generators and scaffolding.&lt;/p&gt;
&lt;h3&gt;Existing solutions&lt;/h3&gt;
&lt;p&gt;The problem with generators is that they don&amp;#8217;t actually solve the problem of duplication in the code &amp;#8211; they simply make it easier to get up and running by generating boilerplate code for you to modify. But once the code is in place it is still something to be maintained.&lt;/p&gt;
&lt;p&gt;The problem with scaffolding is the same problem that plagues the scaffolding that originally came with Rails &amp;#8211; it provides a very generic abstraction with little in the way of flex points which means you usually end up overriding the scaffolded methods anyway. You can usually generate scaffolding directly into the source itself but this has all of the same problems as generators.&lt;/p&gt;
&lt;h3&gt;An alternative approach&lt;/h3&gt;
&lt;p&gt;My approach was to first attempt some small, fine-grained refactorings on my controllers such as &lt;a href="http://www.refactoring.com/catalog/extractMethod.html"&gt;Extract Method&lt;/a&gt; to make sure that the &lt;span class="caps"&gt;REST&lt;/span&gt; actions focussed on their core responsibilities and delegated to template methods for specific behaviour. A common example is the create action, which usually looks something like this:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;def create
  @user = User.new(params[:user])
  if @user.save
    flash[:notice] = 'User created successfully'
    redirect_to user_url(@user)
  else
    flash[:error] = 'User creation failed'
    # some specific error handling
    render :action =&amp;gt; 'new'
  end
rescue SomeException
  # exception handling here
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;After extracting functionality to template methods, I ended up with code like this:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;def create
  user = User.new(params[:user])
  if user.save
    handle_successful_create_for user
  else
    handle_failed_create_for user
  end
rescue
  handle_exception_for_user
end&lt;/code&gt;

&lt;code class="ruby"&gt;protected
  def handle_succesful_create_for user
    flash[:notice] = 'User creeated successfully'
    redirect_to user_url(user)
  end
    
  def handle_failed_create_for user
    @user = user
    flash[:error] = 'User creation failed'
    render :action =&amp;gt; 'new'
  end&lt;/code&gt;

&lt;code class="ruby"&gt;  def handle_exception_for user
    # exception handling here
  end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;The same approach is taken for other methods and after making this refactoring I am now left with identical RESTful actions across my application. Some might feel that the next step was to &lt;a href="http://www.refactoring.com/catalog/extractSuperclass.html"&gt;extract a super-class&lt;/a&gt; but there was still some variation that hadn&amp;#8217;t been eliminated &amp;#8211; each controller worked with a different model. I was also somewhat uneasy about using inheritance to eliminate duplication &amp;#8211; it is often a good idea to favour composition where possible&lt;sup class="footnote"&gt;&lt;a href="#fn1"&gt;1&lt;/a&gt;&lt;/sup&gt;. However a solution that uses composition is awkward because the object model for Rails controllers is abstracted in the framework. Fortunately Ruby provides us with an alternative that allows us to avoid modifying the inheritance tree in the form of &lt;a href="http://www.rubycentral.com/book/tut_modules.html"&gt;mixins&lt;/a&gt;. Using include and extend we are able to add functionality to classes and their instances. This is the approach I decided to take but that still left that pesky model variation to deal with.&lt;/p&gt;
&lt;h3&gt;Refactoring further: extending the &lt;span class="caps"&gt;DSL&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;Once again Ruby comes to the rescue with it&amp;#8217;s excellent meta-programming functionality. We take advantage of that functionality all of the time in Rails when we use it&amp;#8217;s macro-style class methods; filters and ActiveRecord associations and validations are great examples. Using meta-programming, it was relatively easy to come up with a small &lt;span class="caps"&gt;DSL&lt;/span&gt; for exposing resources with a controller. Here is the first part:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;class UsersController &amp;lt; ApplicationController
  expose_resource :user
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;As it clearly states, this tells the UsersController that we want to expose our User model. We can use this value throughout whatever code we write to generate each action. The next thing to tackle was how to generate these actions at runtime. I studied the scaffolding code that comes with Rails but wasn&amp;#8217;t really comfortable with treating code as a template and running it through an eval statement. I decided to take a more object-oriented approach and created a series of &lt;a href="http://en.wikipedia.org/wiki/Command_pattern"&gt;command objects&lt;/a&gt; to represent each action. This solution was easy to test and extend where necessary.&lt;/p&gt;
&lt;p&gt;Finally, there needed to be some way of specifying what actions RESTful actions you want to expose &amp;#8211; you might not want all seven. This lead to the second part of the &lt;span class="caps"&gt;DSL&lt;/span&gt;:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;class UsersController &amp;lt; ApplicationController
  expose_resource :user, :except =&amp;gt; [:update, :destroy]
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;Of course, you might only want a few of the actions:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;class UsersController &amp;lt; ApplicationController
  expose_resource :user, :only =&amp;gt; [:index, :show]
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;So where does that leave us? All of the core functionality is in place and all that the developer has to provide is an implementation of the various callback handlers. With some sensible default implementations to these handlers built-in, the developers life is made even easier.&lt;/p&gt;
&lt;h3&gt;One size does not fit all&lt;/h3&gt;
&lt;p&gt;It is worth noting that there isn&amp;#8217;t a callback handler designed for every situation. Besides the sheer difficulty in guessing all of the flex-points that a developer might need, it would require a lot of bloated unnecessary code just to cater for edge cases. The above abstraction was never designed to cover all cases and I don&amp;#8217;t believe any abstraction ever can. However it does make it possible to add functionality quickly at least 80% of the time and it handling the remaining 20% of cases is as simple as the developer providing their own hardcoded action.&lt;/p&gt;
&lt;p&gt;All of the code discussed above is available as a Rails plugin that I&amp;#8217;ve called restful_exposure. It can be checked out with Subversion at the following &lt;span class="caps"&gt;URL&lt;/span&gt;:&lt;/p&gt;
&lt;pre class="shell"&gt;&lt;code class="shell"&gt;svn://lukeredpath.co.uk/opensource/plugins/restful_exposure/trunk&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;A &lt;span class="caps"&gt;README&lt;/span&gt; is bundled and all code is released under the &lt;a href="http://www.opensource.org/licenses/mit-license.php"&gt;&lt;span class="caps"&gt;MIT&lt;/span&gt; license&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Feedback&lt;/h3&gt;
&lt;p&gt;At present, most of the callback handlers are lacking implementation. This code is only a couple of days old and is &lt;strong&gt;not&lt;/strong&gt; considered production-ready by a long shot. For starters, it is completely lacking any form of test coverage. I used our existing test coverage for our intranet to guide my way during refactoring but it is my plan to add a good suite of RSpec specifications. This isn&amp;#8217;t even live on our intranet yet and currently lives on a branch of our intranet code.&lt;/p&gt;
&lt;p&gt;In addition to the above functionality there are a few other features that came about as a direct result of extracting this from our intranet application. The first is the introduction of a parameterized index action. It is common to have some alternative collection actions as well as index &amp;#8211; archives, search and recent are good examples. They both retrieve a collection of objects but the number of items retrieved, their order and how they are filtered varies. This variation is encapsulated by the index parameter which is passed into ActiveRecord::Base#find as its options hash. This lets you do things like this:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;def recent
  index :limit =&amp;gt; 5
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;Another thing that it was important to handle was nested resources, representing belongs_to/has_x relationships. One of the impacts this had on the code was in the creation of new objects &amp;#8211; the new action had to ensure that the new object created (to be bound to the new form) was linked to its parent object. Getting the parameters for this is made easy by convention and &lt;span class="caps"&gt;URL&lt;/span&gt; parameters made available by nested resource routes so it wasn&amp;#8217;t a problem to extract. This lead to the first option for expose_resource:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;class FilesController &amp;lt; ApplicationController
  expose_resource :file, :nested_under =&amp;gt; :folder
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;The second option came from the need to link new objects to their parent object when working with tree-like nesting of a single model using acts_as_tree. Support for this is built-in using the as_tree option:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;class FolderController &amp;lt; ApplicationController
  expose_resource :folder, :as_tree =&amp;gt; true
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;I&amp;#8217;m well aware that the Rails community is an opionated one (naturally) and that this might not appeal to some people. Some people might perceive this as &amp;#8220;too much magic&amp;#8221;. Personally I&amp;#8217;d disagree &amp;#8211; I think this is no more magic here than has_many and acts_as_foo. It has been extracted from a real working application and I think that is important. I have a &amp;#8220;3 strikes&amp;#8221; rule when it comes to abstractions like this &amp;#8211; if I&amp;#8217;ve done the same thing three different times or on three different apps then its time to look for that abstraction and reduce the amount of code that needs maintaining. This &amp;#8220;3 strikes&amp;#8221;/abstract through extraction approach is exactly the same one that lead to my &lt;a href="http://opensource.agileevolved.com/trac/wiki/CryptedAuthentication"&gt;crypted_authentication&lt;/a&gt; plugin.&lt;/p&gt;
&lt;p&gt;If you are working with RESTful resources and have found yourself typing the same thing over and over again, please do checkout the plugin and give it a try. I repeat, this is &lt;strong&gt;not&lt;/strong&gt; production-ready but I&amp;#8217;m really interested in people&amp;#8217;s feedback on the approach and any suggestions. Do dig into the source to get an understanding of how I&amp;#8217;ve approached the implementation. I&amp;#8217;ve tried to avoid Ruby-fu &amp;#8220;magic&amp;#8221; wherever I can in favour of traditional object-oriented approaches.&lt;/p&gt;
&lt;p&gt;Are there any areas such as the nesting/tree-like structures above that you find yourself doing often that would be a good fit for abstraction? What do you think of the &lt;span class="caps"&gt;API&lt;/span&gt;? Comments are open!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update 05/02/2007&lt;/strong&gt;: As per Steve&amp;#8217;s suggestion in the comments below, I&amp;#8217;ve modified the interface so that it uses just the single expose_resource call, with :only and :except options (:all by default) as per the Rails filters &lt;span class="caps"&gt;API&lt;/span&gt;. I&amp;#8217;ve updated the code samples above to reflect this. expose_resource_actions has been removed.&lt;/p&gt;
&lt;p class="footnote" id="fn1"&gt;&lt;sup&gt;1&lt;/sup&gt; It is a generally accepted object-oriented design rule &amp;#8211; and one that I agree with and try to adhere to &amp;#8211; that composition leads to less coupling and polution of the inheritance tree (which should really only really be used when objects really are of similar type). I also find it leads to more easily testable and elegant solutions.&lt;a href="http://www.google.com/search?client=safari&amp;amp;rls=en&amp;amp;q=%22favour+composition+over+inheritance%22&amp;amp;ie=UTF-8&amp;amp;oe=UTF-8"&gt;On Google&lt;/a&gt;.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=sa-nKM0Zh_I:DlDr6Uqr7lg:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=sa-nKM0Zh_I:DlDr6Uqr7lg:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=sa-nKM0Zh_I:DlDr6Uqr7lg:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=sa-nKM0Zh_I:DlDr6Uqr7lg:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=sa-nKM0Zh_I:DlDr6Uqr7lg:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=sa-nKM0Zh_I:DlDr6Uqr7lg:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=sa-nKM0Zh_I:DlDr6Uqr7lg:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=sa-nKM0Zh_I:DlDr6Uqr7lg:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/sa-nKM0Zh_I" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/refactoring-rest-searching-for-an-abstraction.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/pastures-new.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/s776c4rzYig/pastures-new.html" rel="alternate" />
    <published>2006-12-01T12:53:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>Pastures New</title>
    <content type="html">&lt;p&gt;Last Thursday was my last day working for &lt;a href="http://www.agileevolved.com"&gt;Agile Evolved&lt;/a&gt;. I enjoyed my time at AE and want to say thanks to &lt;a href="http://www.noodlesinmysandals.com"&gt;Jonathan&lt;/a&gt; for employing me in the first place and giving me the opportunity to grow as a programmer, work full-time with Ruby and Rails, contribute to the open-source community and to become an active part of the &lt;a href="http://lrug.org"&gt;London Ruby community&lt;/a&gt;. Good luck to everybody at Agile Evolved!&lt;/p&gt;
&lt;h3&gt;The new job&lt;/h3&gt;
&lt;p&gt;This Monday I started my new job as Head of Software Development for a London-based media company. Unfortunately, at the request of my employers I&amp;#8217;m unable to say who I am working for &amp;#8212; don&amp;#8217;t get excited, it isn&amp;#8217;t anybody you would have heard of anyway (probably) but I am really excited about the challenges I will be facing over the coming months.&lt;/p&gt;
&lt;p&gt;Obviously there is little I can say about what I will be working on at this moment in time but needless to say we will be fully utilizing the power of Ruby and RubyOnRails and other open source software on several large-scale projects in what could be one of the largest-scale deployments of a Ruby/Rails application. Exciting stuff! I&amp;#8217;ll try and post information on how we are using Ruby whenever I possibly can, confidentiality and approval from our PR department pending of course.&lt;/p&gt;
&lt;h3&gt;Project updates&lt;/h3&gt;
&lt;p&gt;Some of you may be wondering where this leaves the numerous open-source projects that I am working on. Naturally, I will be very busy over the coming months but I was keen to stress that how important contributing to open-source software was to my new employers and I have their full support so my involvement will continue pretty much as it is, as will my participation in the Ruby community&amp;#8230;I&amp;#8217;ll have to drag our &lt;span class="caps"&gt;CTO&lt;/span&gt; (aka my boss) down to the next &lt;a href="http://lrug.org"&gt;&lt;span class="caps"&gt;LRUG&lt;/span&gt;&lt;/a&gt; meeting.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://danwebb.net"&gt;Dan&lt;/a&gt; and I will do our best to get a new release of &lt;a href="http://ujs4rails.com"&gt;&lt;span class="caps"&gt;UJS&lt;/span&gt;&lt;/a&gt; out of the door before the end of the year. It is most likely that we will put back any new features to a 0.5 release and concentrate on bugs and Rails 1.2 compatibility for 0.4. My work on &lt;a href="http://activespec.rubyforge.org"&gt;ActiveSpec&lt;/a&gt; will most likely be put off until the new year now. Meanwhile, &lt;a href="http://lazyatom.com"&gt;James&lt;/a&gt; and I are almost ready with the official Rails plugin repository &amp;#8212; it should hopefully be ready in time for the Rails 1.2 final release.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m also happy to announce that I have officially joined the &lt;a href="http://rspec.rubyforge.org"&gt;RSpec&lt;/a&gt; development team. My initial contributions will likely be towards the RSpec TextMate bundle but I hope to get even more involved in the new year.&lt;/p&gt;
&lt;h3&gt;And finally&amp;#8230;&lt;/h3&gt;
&lt;p&gt;On the subject of moving on, I recently moved my blog from &lt;a href="http://site5.com"&gt;Site5&lt;/a&gt; to a &lt;a href="http://rimuhosting.com"&gt;Rimuhosting &lt;span class="caps"&gt;VPS&lt;/span&gt;&lt;/a&gt;. Site5 weren&amp;#8217;t bad for a shared host but they had no &lt;a href="http://mongrel.rubyforge.org"&gt;Mongrel&lt;/a&gt; support and frankly FastCGI and Rails is just painful.&lt;/p&gt;
&lt;p&gt;For those interested in my setup, I have two allocated IP addresses &amp;#8211; I&amp;#8217;m running &lt;a href="http://nginx.net"&gt;Nginx&lt;/a&gt; on one IP as a proxy to Mongrel for any Rails apps (currently just this blog) and lighttpd on the second IP address for serving up any static content or &lt;span class="caps"&gt;PHP&lt;/span&gt; apps that I might need (such as &lt;a href="http://haveamint.com"&gt;Mint Stats&lt;/a&gt; and &lt;a href="http://roundcube.net"&gt;Roundcube Webmail&lt;/a&gt;). The setup works well and was easy to get up and running &amp;#8212; not having to use Apache 2.2 also saves me plenty of resources which is great because I only have 160MB of &lt;span class="caps"&gt;RAM&lt;/span&gt; to play with on my current plan.&lt;/p&gt;
&lt;p&gt;I really recommend a &lt;span class="caps"&gt;VPS&lt;/span&gt; if you are running Rails apps and Rimuhosting are very good value for money. The only pain for me was setting up my mail server with Postfix, Dovecot and SpamAssassin which was a new and painful experience for me. I got there in the end though and I can now have my own Subversion repositories and I will eventually have an instance of Trac up and running for my various open-source projects. Stay tuned!&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=s776c4rzYig:DIxvOXunFQE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=s776c4rzYig:DIxvOXunFQE:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=s776c4rzYig:DIxvOXunFQE:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=s776c4rzYig:DIxvOXunFQE:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=s776c4rzYig:DIxvOXunFQE:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=s776c4rzYig:DIxvOXunFQE:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=s776c4rzYig:DIxvOXunFQE:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=s776c4rzYig:DIxvOXunFQE:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/s776c4rzYig" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/pastures-new.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/rjs-templates-without-the-r.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/BaPnCLZvryc/rjs-templates-without-the-r.html" rel="alternate" />
    <published>2006-11-17T12:02:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>RJS templates without the R!</title>
    <content type="html">&lt;p&gt;In terms of making &lt;span class="caps"&gt;AJAX&lt;/span&gt; applications easier to develop, &lt;a href="http://www.rubyinside.com/16-rjs-resources-and-tutorials-for-rails-programmers-5.html"&gt;&lt;span class="caps"&gt;RJS&lt;/span&gt; templates&lt;/a&gt; are one of the most significant additions to Rails over the past year. They were far more elegant than adding &lt;span class="caps"&gt;AJAX&lt;/span&gt; callback hooks throughout your main &lt;span class="caps"&gt;RHTML&lt;/span&gt; templates (which was just plain ugly) and it let you get access to your model data in your JavaScript response easily. For many, the killer feature was being able to write your &lt;span class="caps"&gt;AJAX&lt;/span&gt; response behaviour in Ruby &amp;#8212; one language to rule them all. The forthcoming &lt;a href="http://www.matthewman.net/articles/2006/09/04/new-rails-feature-simply_helpful"&gt;Simply Helpful&lt;/a&gt; plugin takes this even further. However, not everybody is as keen on &lt;span class="caps"&gt;RJS&lt;/span&gt; templates and a new plugin by &lt;a href="http://danwebb.net"&gt;Dan Webb&lt;/a&gt; seeks to change the way you write your &lt;span class="caps"&gt;AJAX&lt;/span&gt; functionality in Rails.&lt;/p&gt;
&lt;p&gt;Dan and I have had several discussions over a few beers about &lt;span class="caps"&gt;RJS&lt;/span&gt; and Dan being the JavaScript wizard that he is was of the opinion that writing JavaScript behaviour in Ruby was an abstraction too far. His reasoning was simple: JavaScript when used correctly can be a powerful language and adding a Ruby &lt;span class="caps"&gt;API&lt;/span&gt; on top was an extra layer of magic that just wasn&amp;#8217;t necessary. Whilst he made a fair point, I still felt that &lt;span class="caps"&gt;RJS&lt;/span&gt; was infintely better than the old way of doing things and that its pros outweighed its cons&amp;#8230;until now.&lt;/p&gt;
&lt;p&gt;The main con with &lt;span class="caps"&gt;RJS&lt;/span&gt; is that you are depending on another layer and if that layer isn&amp;#8217;t complete you have to revert to JavaScript anyway &amp;#8212; you can do this in &lt;span class="caps"&gt;RJS&lt;/span&gt; by using &lt;code&gt;page.call&lt;/code&gt;&lt;/pre&gt; but this is ugly and really highlights its shortcomings. It would probably be fair to say that &lt;span class="caps"&gt;RJS&lt;/span&gt; as it is currently implemented, is a &lt;a href="http://en.wikipedia.org/wiki/Leaky_abstraction"&gt;leaky abstraction&lt;/a&gt;. Not only that, but &lt;span class="caps"&gt;RJS&lt;/span&gt; is tightly coupled to the &lt;a href="http://prototype.conio.net/"&gt;Prototype&lt;/a&gt; JS library and &lt;a href="http://script.aculo.us/"&gt;Scriptaculous&lt;/a&gt; &amp;#8211; for fans of one of the many other JS core/effects libraries out there you are left with either the option of trying to port &lt;span class="caps"&gt;RJS&lt;/span&gt; to your library of choice, using page.call commands or resorting to the old callbacks method.&lt;/p&gt;
&lt;p&gt;I was somewhat blinded by the &amp;#8220;cool&amp;#8221; factor of writing my JS behaviours in Ruby &amp;#8212; after all I&amp;#8217;m a Ruby programmer first and a JavaScript guy second. But when I really think about it, all &lt;span class="caps"&gt;RJS&lt;/span&gt; does is limit me when I want to do anything outside the simple cases that &lt;span class="caps"&gt;RJS&lt;/span&gt; caters for. But the idea of returning a behavioural response from a template is an excellent one and that has always been the overriding factor for me.&lt;/p&gt;
&lt;p&gt;As of today, a new option has become available in the form of a new plugin courtesy of Dan &amp;#8211; &lt;a href="http://svn.danwebb.net/external/rails/plugins/minus_r/trunk/"&gt;MinusR: &lt;span class="caps"&gt;RJS&lt;/span&gt; templates, without the R&lt;/a&gt;! Thats right, &lt;span class="caps"&gt;RJS&lt;/span&gt; templates that let you write your behaviour in JavaScript instead of Ruby. Not only that, but you can still get access to your model data (or other things such as helpers) by embedding ERb &amp;#8212; MinusR comes with a &lt;code&gt;js()&lt;/code&gt;&lt;/pre&gt; helper method that encodes the output of your ERb output block in &lt;span class="caps"&gt;JSON&lt;/span&gt;. And finally, you can use whatever JS library you like.&lt;/p&gt;
&lt;p&gt;So check it out and start embracing JavaScript. Also check out &lt;a href="http://www.danwebb.net/2006/11/17/rjs-minus-r"&gt;Dan&amp;#8217;s blog post about MinusR&lt;/a&gt;.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=BaPnCLZvryc:3zCMSuh5bTE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=BaPnCLZvryc:3zCMSuh5bTE:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=BaPnCLZvryc:3zCMSuh5bTE:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=BaPnCLZvryc:3zCMSuh5bTE:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=BaPnCLZvryc:3zCMSuh5bTE:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=BaPnCLZvryc:3zCMSuh5bTE:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=BaPnCLZvryc:3zCMSuh5bTE:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=BaPnCLZvryc:3zCMSuh5bTE:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/BaPnCLZvryc" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/rjs-templates-without-the-r.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/exploring-textmate-a-ruby-perspective.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/JDbecQdjERQ/exploring-textmate-a-ruby-perspective.html" rel="alternate" />
    <published>2006-10-30T16:14:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>Exploring TextMate...a Ruby perspective</title>
    <content type="html">&lt;p&gt;&lt;a href="http://platypope.org/blog"&gt;Marshall Vandegrift&lt;/a&gt; has posted an &lt;a href="http://platypope.org/yada/emacs-demo/"&gt;interesting screencast&lt;/a&gt; showing off &lt;a href="http://en.wikipedia.org/wiki/Emacs"&gt;Emacs&lt;/a&gt; as a tool for Ruby/Rails development. I enjoyed the screencast, and Emacs certainly has cool features but all I could think whilst watching it was (&lt;span class="caps"&gt;WARNING&lt;/span&gt;: &lt;span class="caps"&gt;FLAMEBAIT&lt;/span&gt; &lt;span class="caps"&gt;ALERT&lt;/span&gt;!)&amp;#8230;&amp;#8220;urgh, its soooo ugly, how could anybody work with that all day. And TextMate does all of this and in a much nicer way&amp;#8230;&amp;#8221;.&lt;/p&gt;
&lt;p&gt;It got me thinking about how I use TextMate every day and whether I really take full advantage of its built-in commands and snippets for Ruby and Rails development. I wondered how many of the features in the above Emacs screencast were already in TextMate and the answer was&amp;#8230;most of them! This inspired write a post about some of my favorite TextMate snippets and hopefully you&amp;#8217;ll find out about a few you didn&amp;#8217;t know about.&lt;/p&gt;
&lt;p&gt;Whether you are a new or seasoned TextMate user, its really worth spending some time to learn some of the more useful snippets in the bundles for the languages that you are using. I&amp;#8217;m sure most Ruby and Rails developers are aware of the basics such as do..end completion and the various snippets for creating classes, modules and methods. What follows is an overview of some of the cooler functionality built into the Ruby/Rails bundles that you might not have come across. There is a certain amount of mental overhead in learning these snippets but if you can you will find yourself being even more productive.&lt;/p&gt;
&lt;h3&gt;But first&amp;#8230;&lt;/h3&gt;
&lt;p&gt;If you are running a stock TextMate install, you&amp;#8217;ll only have the default set of bundles. There is a wealth of bundles in the &lt;a href="http://macromates.com/svn/Bundles/trunk/"&gt;Macromates Subversion repository&lt;/a&gt;. I won&amp;#8217;t repeat the &lt;a href="http://macromates.com/wiki/Main/SubversionCheckout"&gt;installation instructions&lt;/a&gt; here, but I will mention a small tip for managing your bundles.&lt;/p&gt;
&lt;p&gt;The Macromates repository has a &lt;strong&gt;lot&lt;/strong&gt; of bundles and the chances are you will only need a small selection of them. Installing all the bundles slows down TextMate&amp;#8217;s opening times and adds a lot of noise to the Bundles/Snippets menus. Instead of simply checking out all of the bundles into your /Library/Application Support/TextMate folder, create a folder in a Subversion repository of your own, and check the empty folder out to /Library/Application Support/TextMate. Now, add all the folders in the &lt;a href="http://macromates.com/svn/Bundles/trunk/"&gt;Macromates repository&lt;/a&gt; &lt;strong&gt;except&lt;/strong&gt; the Bundles folder to your repository as an svn:externals entry. Next, create a Bundles folder and check this in. Finally, add all of the Bundles that you want as svn:externals entries in the Bundles folder.&lt;/p&gt;
&lt;p&gt;If the above sounds too complicated or time-consuming, you can use &lt;a href="http://opensource.agileevolved.com/svn/root/textmate/libraries/luke/"&gt;my own setup&lt;/a&gt; as a starting point.&lt;/p&gt;
&lt;h3&gt;Syntax constructs&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; for the rest of this article, I will put any relevant tab-triggers in square brackets.&lt;/p&gt;
&lt;p&gt;The TextMate Ruby bundle has snippets for most of the common declarations that you are likely to use&amp;#8230;there are definitions for classes &lt;strong&gt;[cla]&lt;/strong&gt;, modules &lt;strong&gt;[mod]&lt;/strong&gt; and methods &lt;strong&gt;[def]&lt;/strong&gt; and other common blocks such as conditional statements &lt;strong&gt;[if, ife, elsif]&lt;/strong&gt;, loops &lt;strong&gt;[until, while]&lt;/strong&gt;, exception handling &lt;strong&gt;[begin]&lt;/strong&gt; and generic blocks &lt;strong&gt;[do]&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The class and module snippets, like a lot of newer snippets, use a single tab expansion for multiple snippets, with the different options appearing as a contextual menu. This reduces the overhead of learning lots of obscure triggers and gives you a good overview of what you can do with a particular trigger. The class snippets, for example, have variations for creating standard classes, classes with inheritance, classes with a generated initialize template and more. You can even create a new Test::Unit testcase, complete with the necessary requires, Test::Unit::TestCase inheritance and a default test method with a single trigger &lt;strong&gt;[tc]&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Finally, there are snippets for many of the common tasks performed when writing a Ruby script, such as entering the shebang line &lt;strong&gt;[rb]&lt;/strong&gt;, requiring files &lt;strong&gt;[req]&lt;/strong&gt;, generating symbol =&amp;gt; value key/value pairs for hashes &lt;strong&gt;[:]&lt;/strong&gt;, lambda blocks &lt;strong&gt;[lam]&lt;/strong&gt;, and one of my favorites, path-from-here &lt;strong&gt;[patfh]&lt;/strong&gt; which is used for getting the path relative to the current script and generates:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;File.join(File.dirname(__FILE__), *%w&amp;lt;strong&amp;gt;[rel path here]&amp;lt;/strong&amp;gt;)&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;h3. Writing classes/modules&lt;/p&gt;
&lt;p&gt;In addition to the snippets for defining classes and modules, there are some other goodies too.&lt;/p&gt;
&lt;p&gt;If you want to add &lt;a href="http://ruby-doc.org/core/classes/Enumerable.html"&gt;Enumerable&lt;/a&gt; support to your class, you need to include the Enumerable module and define an each method. To do this in Textmate, simply create a new class then inside your class definition, use the Enumerable snippet &lt;strong&gt;[Enum]&lt;/strong&gt;. This will generate something like this for you:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;class Foo
  include Enumerable&lt;/code&gt;

&lt;code class="ruby"&gt;  def each(&amp;amp;block)&lt;/code&gt;

&lt;code class="ruby"&gt;  end
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;In a similar fashion, the Comparable snippet &lt;strong&gt;[Comp]&lt;/strong&gt; will include the Comparable module and create a stub &amp;lt;=&amp;gt; method for you to fill in.&lt;/p&gt;
&lt;p&gt;Defining attributes for your class is a breeze &amp;#8211; TextMate contains snippets for attr_reader &lt;strong&gt;&amp;#174;&lt;/strong&gt;, attr_writer &lt;strong&gt;[w]&lt;/strong&gt; and attr_accessor &lt;strong&gt;[rw]&lt;/strong&gt;. You can define class methods just as easily as you define instance methods &lt;strong&gt;[defs]&lt;/strong&gt;, alias methods &lt;strong&gt;[am]&lt;/strong&gt; and overwrite method_missing &lt;strong&gt;[mm]&lt;/strong&gt;. Don&amp;#8217;t forget the singleton class for helping out with that meta-programming goodness &lt;strong&gt;[sin]&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;Other cool snippets&lt;/h3&gt;
&lt;p&gt;There are plenty of snippets for all of your favorite iterators including each &lt;strong&gt;[ea]&lt;/strong&gt;, each_pair &lt;strong&gt;[eap]&lt;/strong&gt;, collect/map &lt;strong&gt;[col/map]&lt;/strong&gt; and everybody&amp;#8217;s favorite, inject &lt;strong&gt;[inj]&lt;/strong&gt;. There are too many to mention here &amp;#8211; there are snippets for all of the common iterators and block methods defined in Enumerable and other collection classes, as well as snippets for iterating over files.&lt;/p&gt;
&lt;p&gt;How often do you fire up &lt;span class="caps"&gt;IRB&lt;/span&gt; just to run a simple snippet of code? I do&amp;#8230;but why waste time loading up &lt;span class="caps"&gt;IRB&lt;/span&gt; when you can execute lines of Ruby directly in TextMate? Simply write a line of Ruby code, then run the &amp;#8220;Execute line as Ruby&amp;#8221; command &lt;strong&gt;[Ctrl + Shift + E]&lt;/strong&gt;. The result of the expression will be inserted directly on the next line.&lt;/p&gt;
&lt;p&gt;Finally, some personal favorites. Trying to convert all those string keys to symbols in your Rails app to make things much easier to read? TextMate makes it easy to switch between strings and symbols &amp;#8211; simply move the cursor onto the string/symbol you want to switch and hit &lt;strong&gt;[Ctrl + :]&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Interested to know whats going on under the hood of the library that you&amp;#8217;ve required in your script? Put the cursor over the require line, and hit &lt;strong&gt;[Cmd + Shift + D]&lt;/strong&gt;. If TextMate can find the required file, it will open it for you. How cool is that?&lt;/p&gt;
&lt;p&gt;A final, Rails-related mention goes to the Rails migration snippets. There are only three commands you need to know: columns, tables and indexes &lt;strong&gt;[mcol, mtab, mind]&lt;/strong&gt;. All of the common migration commands are supported and where possible, the snippets will generate your self.down method for you, whilst you are writing the self.up method! A particularly good example of this is &amp;#8220;Drop and Create table&amp;#8221; &amp;#8211; type in the name of the table you want to drop, hit tab twice and it will populate self.down using the relevant section of your schema.rb. Clever stuff.&lt;/p&gt;
&lt;h3&gt;Writing your own bundles&lt;/h3&gt;
&lt;p&gt;Of course, the real power behind TextMate lies not only in its pretty good set of existing bundles, but the ability to write your own. Writing snippets is the easiest of the lot and I really advise you to create your own &amp;#8220;personal&amp;#8221; bundle to store all of the little snippets tailor-made for you. The next step up is writing commands and here is the killer feature &amp;#8211; you can write your commands in whatever language you know best, as long as TextMate can find the interpreter (it uses a shebang line at the top of your command). A lot of the built-in commands are written in Bash, but many of the Ruby commands are written in, unsurprisingly, Ruby. Take a look at some of the built-in commands to give you an idea of what you can do.&lt;/p&gt;
&lt;p&gt;Here are some of the bundles that I&amp;#8217;ve written that are available for all:&lt;/p&gt;
* &lt;a href="http://opensource.agileevolved.com/svn/root/textmate/bundles/RSpec.tmbundle/"&gt;RSpec TextMate Bundle&lt;/a&gt;
* &lt;a href="http://opensource.agileevolved.com/svn/root/textmate/bundles/RESTfulRails.tmbundle/"&gt;RESTful Rails &amp;#8211; snippets for RESTful Rails development&lt;/a&gt;
* &lt;a href="http://opensource.agileevolved.com/svn/root/textmate/bundles/RESTfulRails.tmbundle/"&gt;UJS4Rails Bundle &amp;#8211; snippets for the &lt;span class="caps"&gt;UJS&lt;/span&gt; Rails plugin&lt;/a&gt;
&lt;p&gt;The last two are still very much under development but the RSpec bundle is pretty comprehensive and covers all of the built-in expectations, as well as the mocking &lt;span class="caps"&gt;API&lt;/span&gt;, Rails integration and spec runners.&lt;/p&gt;
&lt;h3&gt;Feedback&lt;/h3&gt;
&lt;p&gt;Have you got any favorite snippets that you want to share with the world? Let me know. You can use the comments below, although I advise for anything but short snippets that you &lt;a href="http://pastie.caboo.se"&gt;pastie&lt;/a&gt; them and post the link.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=JDbecQdjERQ:igNUbmRi4vE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=JDbecQdjERQ:igNUbmRi4vE:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=JDbecQdjERQ:igNUbmRi4vE:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=JDbecQdjERQ:igNUbmRi4vE:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=JDbecQdjERQ:igNUbmRi4vE:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=JDbecQdjERQ:igNUbmRi4vE:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=JDbecQdjERQ:igNUbmRi4vE:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=JDbecQdjERQ:igNUbmRi4vE:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/JDbecQdjERQ" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/exploring-textmate-a-ruby-perspective.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/blogging-for-britain.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/Xyp6GuWhhNg/blogging-for-britain.html" rel="alternate" />
    <published>2006-10-17T09:16:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>Blogging for Britain</title>
    <content type="html">&lt;p&gt;An &lt;a href="http://news.bbc.co.uk/1/hi/technology/6048392.stm"&gt;interesting article&lt;/a&gt; dropped into my newsreader this morning. The &lt;a href="http://www.nationaltrust.org.uk/"&gt;National Trust&lt;/a&gt; are organising what they hope to be &amp;#8220;Britain&amp;#8217;s biggest blog&amp;#8221; by encouraging people to blog about a normal day living in Britain. The blog entries will then be archived and stored in the &lt;a href="http://www.bl.uk/"&gt;British Library&lt;/a&gt; amongst other places.&lt;/p&gt;
&lt;p&gt;As a relatively new blogger (this blog is my fourth attempt at getting a blog going and has been up and running since May this year), I&amp;#8217;ve tried to avoid blogging about the mundane day-to-day aspects of life but I have to admit that this sounds like an intriguing idea that I&amp;#8217;d really like to have a go at.&lt;/p&gt;
&lt;p&gt;If you live in the UK and you&amp;#8217;d like to take part yourself, entries can be uploaded between 17 &amp;#8211; 31 October on the &lt;a href="http://www.historymatters.org.uk/output/page96.asp"&gt;History Matters&lt;/a&gt; website. It doesn&amp;#8217;t have to be more than 650 words long. I&amp;#8217;ll be picking a day in the coming fortnight to give it a go (hopefully an interesting day!) and I&amp;#8217;ll post my entry up on here too.&lt;/p&gt;
&lt;p&gt;Go on, &lt;a href="http://www.historymatters.org.uk/output/page96.asp"&gt;blog for Britain!&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=Xyp6GuWhhNg:JpKQHKL9iOw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=Xyp6GuWhhNg:JpKQHKL9iOw:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=Xyp6GuWhhNg:JpKQHKL9iOw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=Xyp6GuWhhNg:JpKQHKL9iOw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=Xyp6GuWhhNg:JpKQHKL9iOw:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=Xyp6GuWhhNg:JpKQHKL9iOw:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=Xyp6GuWhhNg:JpKQHKL9iOw:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=Xyp6GuWhhNg:JpKQHKL9iOw:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/Xyp6GuWhhNg" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/blogging-for-britain.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/introduction-to-activespec.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/lvL9KLHJTKw/introduction-to-activespec.html" rel="alternate" />
    <published>2006-09-28T11:39:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>Introduction to ActiveSpec</title>
    <content type="html">&lt;p&gt;As I mentioned in a previous article, during my downtime last week I have been working on a Ruby library called ActiveSpec. Other than a quiet announcement on the Rails mailing list (and an accidental one on the Rails core list) little has been said about it, until now.&lt;/p&gt;
&lt;h3&gt;Specifications&lt;/h3&gt;
&lt;p&gt;Not to be confused with the kind of specifications we talk about when using the RSpec &lt;span class="caps"&gt;BDD&lt;/span&gt; framework, the Specification design and analysis pattern can form a part of your core domain model. The easiest way of describing a specification is a collection of rules. Objects can be evaluated against the rules of a specification and should all of those rules be met, the specification is satisfied. The Specification pattern can be used to decouple those rules from your core domain objects. This can be especially useful when the evaluation of a series of business rules depends on several domain objects at one time.&lt;/p&gt;
&lt;p&gt;The Specification pattern borrows from logic-programming and the concept of predicates &amp;#8211; a specification object is a an object that essentially evaluates to a boolean. Different specification objects can then be combined to create more complex specifications (a composite). Domain Driven Design, by Eric Evans, concisely summarizes this as follows:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;#8220;Create explicit predicate-like &lt;span class="caps"&gt;VALUE&lt;/span&gt; &lt;span class="caps"&gt;OBJECTS&lt;/span&gt; for specialised purposes. A &lt;span class="caps"&gt;SPECIFICATION&lt;/span&gt; IS A &lt;span class="caps"&gt;PREDICATE&lt;/span&gt; that determines if an object does or does not satisfy some criteria.&amp;#8221;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There are three main uses of the Specification pattern:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;Validation&lt;/li&gt;
	&lt;li&gt;Selection&lt;/li&gt;
	&lt;li&gt;Creation (build-to-order)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The simplest of these is validation and that is what the initial release of ActiveSpec focuses on.&lt;/p&gt;
&lt;h3&gt;What is ActiveSpec?&lt;/h3&gt;
&lt;p&gt;ActiveSpec is a Ruby implementation of the Specification pattern. At the lowest level, it provides a series of generic Specifications that will be familiar to anybody who has used Rails validation macros. It also provides a way of creating composite specifications in a more declarative manager and on top of that, a simple &lt;span class="caps"&gt;DSL&lt;/span&gt; that can be used for easily defining specifications. Finally, it makes it easy to declaratively link your own classes to specifications depending on context.&lt;/p&gt;
&lt;h3&gt;Where would you use it?&lt;/h3&gt;
&lt;p&gt;As I mentioned before, one of the main uses of the Specification is for validation. More specifically, it can be used for evaluating objects, or a combination of objects against a series of complex business rules. Validation was the initial motivation for creating ActiveSpec and it was designed as an accompaniment &amp;#8211; not a replacement &amp;#8211; for ActiveRecord&amp;#8217;s built-in validation macros. Furthermore, ActiveSpec is not limited to use with ActiveRecord or indeed, Rails. It can be used with &lt;strong&gt;any&lt;/strong&gt; Ruby object.&lt;/p&gt;
&lt;p&gt;ActiveRecord&amp;#8217;s validation macros make it easy to declaratively add data validation to your model objects. A lot of the time these are enough and for basic validation (such as size constraints) they work well. On a recent project, with a particularly complex domain model, there was the need to validate objects in different ways, depending on its state and relationship with other objects. We found there was a lot of pushback as we tried to bend ActiveRecord validations to apply the complex business rules needed. We managed it, but the resulting code wasn&amp;#8217;t pretty and certainly not the elegant, clear code you would come to expect from a Ruby or Rails application. In retrospect, the Specification pattern would have been an ideal solution.&lt;/p&gt;
&lt;h3&gt;Getting started with ActiveSpec&lt;/h3&gt;
&lt;p&gt;Before we can do anything, we need to install ActiveSpec. This can be done easily using RubyGems.&lt;/p&gt;
&lt;pre class="shell"&gt;&lt;code class="shell"&gt;$ gem install activespec&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&amp;#8217;ll start by taking a look at the ActiveSpec::Specifications module. This module contains low-level, generic Specifications, that match a lot of the functionality provided by ActiveRecord&amp;#8217;s validation macros. These include:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;CollectionSpecification (validates_inclusion_of and validates_exclusion_of)&lt;/li&gt;
	&lt;li&gt;ConfirmationSpecification (validates_confirmation_of)&lt;/li&gt;
	&lt;li&gt;PresenceSpecification (validates_presence_of)&lt;/li&gt;
	&lt;li&gt;SizeSpecification (validates_size_of)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All specifications in ActiveSpec conform to the same, simple one-method interface, &lt;code&gt;satisfied_by?&lt;/code&gt; In addition, all of the above Specifications can be used against multiple attributes of an object, in a similar way to the ActiveRecord validations macros. Here&amp;#8217;s SizeSpecification in action:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;user = User.new(:username =&amp;gt; 'luke')
spec = SizeSpecification.new(6, :username)
spec.satisfied_by? user
#=&amp;gt; false&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On the face of it, this offers nothing different to using &lt;code&gt;validates_size_of :user, :is =&amp;gt; 6&lt;/code&gt; On their own, these low-level Specifications offer little, except for an alternative implementation of the built-in ActiveRecord validations. Its when you start combining them with other low-level specifications, as well as your own custom specification objects, to create more complex specifications that their power becomes more obvious.&lt;/p&gt;
&lt;h3&gt;Composite specifications&lt;/h3&gt;
&lt;p&gt;To aid in the creation of composite specifications (a collection of specifications that can be treated as an individual specification due to a matching interface), ActiveSpec provides a CompositeSpecification class:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;spec = CompositeSpecification.new
spec.add_specification(SizeSpecification.new(6, :username))
spec.add_specification(CollectionSpecification.new(18..30, :age))
spec.add_specification(ConfirmationSpecification.new(:password))
spec.satisfied_by?(User.new)
#=&amp;gt; false&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Of course, because a CompositeSpecification and a single Specification share the same interface, there is no reason why you can&amp;#8217;t add composite specifications to other composite specifications.&lt;/p&gt;
&lt;p&gt;Finally, ActiveSpec provides two low-level convenience classes. The first is a decorator called NotSpecification. You can use this to reverse the result of a specification (i.e. if a specification returns true, then it will return false when wrapped in a NotSpecification decorator). One potential application of this is in the implementation of both &lt;code&gt;validates_exclusion_of&lt;/code&gt;&lt;/pre&gt; using the CollectionSpecification:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;spec = CollectionSpecification.new(1..10, :age)
# or validates_inclusion_of :age, :in =&amp;gt; 1..10&lt;/code&gt;

&lt;code class="ruby"&gt;spec_two = NotSpecification.new(CollectionSpecification.new(1..10, :age))
# or validates_exclusion_of :age, :in =&amp;gt; 1..10&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;The second convenience is a simple adapter that can be used to wrap around Proc objects to make them conform to the specification interface. This makes it possible to use Procs as ad-hoc specifications:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;spec = ProcSpecification.new(proc{ |object|
	# do something with object
	# and return true or false
})
spec.satisfied_by? some_object&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Taking things further with ActiveSpec::Base&lt;/h3&gt;
&lt;p&gt;So far we&amp;#8217;ve demonstrated some of the built-in ActiveSpec generic specifications can be used in place of ActiveRecord validations and how they can be combined to create more complicated specifications. Whilst this forms the core of ActiveSpec&amp;#8217;s functionality, using these low-level specifications directly isn&amp;#8217;t very elegant. ActiveSpec::Base allows you to construct composite specifications in a much more declarative manner that will feel a lot more comfortable for those used to ActiveRecord validations. All you need to do is inherit from ActiveSpec::Base.&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;class UserSpecification &amp;lt; ActiveSpec::Base
	requires_presence_of :username, :password
	requires_size 6, :password
	requires_confirmation_of :password
	requires_inclusion_in 18..30, :age
end&lt;/code&gt;

&lt;code class="ruby"&gt;UserSpecification.satisfied_by?(some_user)&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;For every built-in low-level specification, there is an equivalent &lt;code&gt;must_satisfy&lt;/code&gt; You can also pass in a block to must_satisfy and it will automatically be converted into a ProcSpecification.&lt;br /&gt;
	&lt;br /&gt;
bc(ruby). class AdvancedUserSpecification &amp;lt; ActiveSpec::Base&lt;br /&gt;
  must_satisfy :valid_user_specification&lt;br /&gt;
  must_satisfy do |user|&lt;br /&gt;
		# do something with user&lt;br /&gt;
	end&lt;br /&gt;
end&lt;/p&gt;
&lt;p&gt;Finally, the ActiveSpec::Context module defines a &lt;code&gt;specification&lt;/code&gt;&lt;/pre&gt; method will be made available in the global namespace. Using it couldn&amp;#8217;t be simpler:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;specification :valid_user do
  requires_presence_of :username, :password
  requires_confirmation_of :password
  must_satisfy :another_specification
end&lt;/code&gt;

&lt;code class="ruby"&gt;ValidUserSpecification.satisfied_by?(some_user)&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;As you can see from the above example, all of the macros available in ActiveSpec::Base are available to the &lt;span class="caps"&gt;DSL&lt;/span&gt;. The &lt;span class="caps"&gt;DSL&lt;/span&gt; will automatically create a specification class that inherits from ActiveSpec::Base which can then be used in the same way as your other specifications.&lt;/p&gt;
&lt;h3&gt;Using the Satisfies mixin for easy integration&lt;/h3&gt;
&lt;p&gt;The final piece of the ActiveSpec puzzle is the ActiveSpec::Satisfies mixin which makes it possible to attach specifications directly to a class. Whilst this increases coupling, there are times when you always want a class to pass one or more specifications and manually passing objects into specification objects can get quite tedious. The ActiveSpec::Satisfies mixin adds two new methods to your class &amp;#8211; a class method called &lt;code&gt;satisfies_specs?&lt;/code&gt; If the use isn&amp;#8217;t obvious, allow me to demonstrate:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;class User
  must_satisfy :valid_user_specification&lt;/code&gt;

&lt;code class="ruby"&gt;  # optional :if argument takes a symbol or a block
  # and can be used for conditional evaluation of
  # specifications
  must_satisfy :activated_user_specification, :if =&amp;gt; :activated?
end&lt;/code&gt;

&lt;code class="ruby"&gt;user = User.new
user.satisfies_specs?
# evaluates ValidUserSpecification
user.activated = true
user.satisfies_specs?
# evaluates both specifications&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;If you want to use this functionality in your Rails models, add this to your environment.rb file:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;require 'active_spec'
ActiveRecord::Base.send(:include, ActiveSpec::Satisfies)&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;There are plans to make ActiveRecord integration easier in the future with the use of a Rails plugin.&lt;/p&gt;
&lt;h3&gt;Give it a try!&lt;/h3&gt;
&lt;p&gt;ActiveSpec is still in its early stages &amp;#8211; its only at version 0.1. The next release will focus on improvements to the existing code and tackling the second use of specifications for selection. Work on this is still in its early stages, but here is a small teaser:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;# pure ruby selection
User.find_by_specification(SomeAdvancedUserSpecification)&lt;/code&gt;

&lt;code class="ruby"&gt;# specifications for SQL queries?
User.find(:all, :conditions =&amp;gt; SomeAdvancedUserSpecification.to_sql)&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;If you have some cool ideas for ActiveSpec, or feedback on any of the above, please let me know.&lt;/p&gt;
&lt;h3&gt;Further information&lt;/h3&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://activespec.rubyforge.org"&gt;ActiveSpec Rubyforge Homepage&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://docs.britsonrails.org.uk/activespec/"&gt;ActiveSpec &lt;span class="caps"&gt;API&lt;/span&gt; Documentation&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.martinfowler.com/apsupp/spec.pdf#search=%22martin%20fowler%20specification%22"&gt;Martin Fowler Specification Article&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://domaindrivendesign.org/book/"&gt;Domain Driven Design book by Eric Evans&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.blog.notesonrails.com/articles/2006/09/27/more-on-activespec"&gt;Carlos Gabaldon looks at integrating ActiveSpec with Rails&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=lvL9KLHJTKw:EndMQXMO-OI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=lvL9KLHJTKw:EndMQXMO-OI:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=lvL9KLHJTKw:EndMQXMO-OI:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=lvL9KLHJTKw:EndMQXMO-OI:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=lvL9KLHJTKw:EndMQXMO-OI:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=lvL9KLHJTKw:EndMQXMO-OI:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=lvL9KLHJTKw:EndMQXMO-OI:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=lvL9KLHJTKw:EndMQXMO-OI:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/lvL9KLHJTKw" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/introduction-to-activespec.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/railsconf-europe-2006-in-review.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/ZyA7F7XuGc8/railsconf-europe-2006-in-review.html" rel="alternate" />
    <published>2006-09-27T16:45:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>RailsConf Europe 2006: In Review</title>
    <content type="html">&lt;p&gt;RailsConf Europe was really great for the most part. There were some awesome keynote talks (sorry, &amp;#8220;plenary&amp;#8221;) with particular highlights being Dave Thomas&amp;#8217; excellent closing talk (which fully deserved the standing-ovation it deserved. Kathy Sierra also gave an awesome talk on creating passionate users. Both were the most confident and professional speakers of the two days but there were many other great talks as well, especially on the Thursday.&lt;/p&gt;
&lt;p&gt;A personal highlight was seeing how popular &lt;a href="http://www.danwebb.net"&gt;Dan&amp;#8217;s&lt;/a&gt; &lt;a href="http://www.ujs4rails.com"&gt;&lt;span class="caps"&gt;UJS&lt;/span&gt;&lt;/a&gt; talk was. I&amp;#8217;ve already &lt;a href="http://www.lukeredpath.co.uk/2006/9/15/ujs-railsconf-europe-2006"&gt;spoke about the talk&lt;/a&gt; but I must restate how cool it was to see the main hall fill out during Friday lunchtime after Dan was asked to repeat his talk due to so many people being disappointed on the Thursday morning. Speaking of &lt;span class="caps"&gt;UJS&lt;/span&gt;, we hope to have 0.4 out soon but a combination of my lack of net access last week and the both of us being really busy means that it might be a few more weeks yet. We have fixed a good few bugs however so we might push a point release in the meantime; I haven&amp;#8217;t decided yet.&lt;/p&gt;
&lt;p&gt;Gavin Bell and Tom Armitage (who I&amp;#8217;d seen give an excellent talk at &lt;a href="http://lrug.org"&gt;&lt;span class="caps"&gt;LRUG&lt;/span&gt;&lt;/a&gt;) gave an interesting talk on polymorphism as a pattern in social software. I felt that perhaps their overall point got lost a little but it was interesting nonetheless. It was cool to see testing with Javascript getting a good airing by &lt;a href="http://mir.aculo.us"&gt;Thomas Fuchs&lt;/a&gt; (&lt;a href="http://www.lukeredpath.co.uk/2006/9/14/thomas-fuchs-adventures-in-javascript-testing"&gt;see my live notes on his talk&lt;/a&gt;) and Marcel Molina Jr. showed some useful tips and tricks for sharing and reusing &lt;span class="caps"&gt;RJS&lt;/span&gt; code. An unexpected highlight of Marcel&amp;#8217;s talk came towards the end when &lt;a href="http://loudthinking.com"&gt;&lt;span class="caps"&gt;DHH&lt;/span&gt;&lt;/a&gt; and &lt;a href="http://www.bofh.org.uk/"&gt;Piers Cawley&lt;/a&gt; got into a small disagreement over the separation of concerns between models and views &amp;#8211; for the record I think &lt;span class="caps"&gt;DHH&lt;/span&gt; was spot on and I was somewhat confused at Piers&amp;#8217; ranting. It was entertaining though.&lt;/p&gt;
&lt;p&gt;&lt;a href="/assets/2006/9/27/weirchnotes_railsconf_large.jpg"&gt;&lt;img src="/assets/2006/9/27/werichnotes_railsconf_small.jpg" alt="Photo of notes" title="My notes from Jim Weirich's RailsConf Europe 2006 talk - click for larger version" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Apart from DHH&amp;#8217;s opening address, which I&amp;#8217;ll come to shortly, the other talk that sticks in mind was Jim Weirich&amp;#8217;s talk. He gave a great overview on &amp;#8220;playing safe with others&amp;#8221; and how to write good Ruby code without unintended side-effects. I&amp;#8217;m too lazy to write up my notes, but you can click on the image above for a larger view of my &lt;a href="http://www.moleskine.com/"&gt;Moleskine&lt;/a&gt; scribblings &amp;#8211; sorry about the dodgy handwriting!&lt;/p&gt;
&lt;p&gt;David Heinemeier Hansson gave a great talk to kick the conference off, which was mostly about code (with all of his slides in Textmate as he didn&amp;#8217;t have Keynote on his laptop&amp;#8230;cool!) and I have to say that I&amp;#8217;m finally convinced about ActiveResource and &lt;span class="caps"&gt;REST&lt;/span&gt;. Simply Helpful looked really neat as well. The one part of David&amp;#8217;s talk that I didn&amp;#8217;t get to see was the part he had to postpone until the core team chat later in the day (I was already on the way home for an early night by that point). I didn&amp;#8217;t see it, but its hard not to know all about it. I&amp;#8217;m sure you&amp;#8217;ve all seen &lt;a href="http://flickr.com/photos/rooreynolds/243810133/"&gt;the photo&lt;/a&gt;. So what was my reaction? To be honest, disappointment.&lt;/p&gt;
&lt;p&gt;I know David likes to make controversial statements in this way (we all remember &amp;#8220;Fuck You&amp;#8221;). He&amp;#8217;s one strongly opinionated guy and in many ways it can be a positive quality (one that comes across heavily in Rails). And in some ways I agree with him &amp;#8211; just because you download a piece of open source software it doesn&amp;#8217;t mean you are entitled to anything. You aren&amp;#8217;t entitled to support, developers aren&amp;#8217;t obliged to listen to your feature requests and ideas and they certainly don&amp;#8217;t have to listen to (or deserve) unconstructive criticism and bitching. However well you thought the recent Rails security hole was handled there was no call for some of the petty bitching, moaning and whining that some people felt the need to do. Constructive criticism is fine and should be encouraged but whining helps nobody.&lt;/p&gt;
&lt;p&gt;What I don&amp;#8217;t agree with is David&amp;#8217;s black and white attitude towards people. I don&amp;#8217;t like the notion of &amp;#8220;if you haven&amp;#8217;t contributed back to the community then I don&amp;#8217;t care what you have to say&amp;#8221;. Just because somebody might be new to the community or hasn&amp;#8217;t had the time to contribute much to the community be it in the form of patches, plugins, or help on the mailing list it shouldn&amp;#8217;t meant that they should just be ignored by default. David may well have no legal responsibility towards anybody who downloads and uses Rails &amp;#8211; a point he has been quite clear about. But in my most humblest of opinions, when you put out a piece of open source software like Rails, promote its use and see it grow into what it has become today, with many developers and companies livelihoods depending on Rails you carry a certain degree of &lt;em&gt;moral&lt;/em&gt; responsibility to those developers. Giving a big &amp;#8220;fuck you&amp;#8221; to people just because they haven&amp;#8217;t yet made some kind of contribution back to the community is short-sighted and somewhat immature and I was disappointed not so much by the point David had to make, but the way in which he made it and had I been around for the core panel talk I&amp;#8217;m sure I would have left with a somewhat bitter taste in the mouth.&lt;/p&gt;
&lt;p&gt;After that last sentence it would be somewhat ironic for me to end my RailsConf thoughts on that note so let me get back to being positive. The conference was well organised (except for those shitty passes) and most of the talks were interesting. Above all else, I had a great two days and got to meet some great people. &lt;a href="http://iamrice.org"&gt;Damien&lt;/a&gt;, &lt;a href="http://new-bamboo.co.uk"&gt;Max&lt;/a&gt;, &lt;a href="http://robbyonrails.com"&gt;Robby&lt;/a&gt;, &lt;a href="http://david.goodlad.ca/"&gt;David&lt;/a&gt;, &lt;a href="http://jlaine.net/"&gt;Jarkko&lt;/a&gt;, &lt;a href="http://www.imajes.info/"&gt;James&lt;/a&gt;, all the guys at &lt;a href="http://www.aemedia.com/aemedia/index.html"&gt;Aegis Media&lt;/a&gt; and everybody else I met over those two crazy days. It was great to meet you guys and share some beers! Here&amp;#8217;s to &lt;a href="http://conferences.oreillynet.com/railseurope/"&gt;RailsConf Europe 2007&lt;/a&gt; &amp;#8211; see you in Berlin!&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=ZyA7F7XuGc8:fKP6mBdNzVw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=ZyA7F7XuGc8:fKP6mBdNzVw:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=ZyA7F7XuGc8:fKP6mBdNzVw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=ZyA7F7XuGc8:fKP6mBdNzVw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=ZyA7F7XuGc8:fKP6mBdNzVw:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=ZyA7F7XuGc8:fKP6mBdNzVw:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=ZyA7F7XuGc8:fKP6mBdNzVw:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=ZyA7F7XuGc8:fKP6mBdNzVw:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/ZyA7F7XuGc8" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/railsconf-europe-2006-in-review.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/give-me-my-internet-back.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/4icQoUlyO_Y/give-me-my-internet-back.html" rel="alternate" />
    <published>2006-09-27T16:30:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>Give me my Internet back!</title>
    <content type="html">&lt;p&gt;Just in case you&amp;#8217;ve been wondering where I&amp;#8217;ve been&amp;#8230;allow me to explain. Warning: ranting ahead!&lt;/p&gt;
&lt;p&gt;Last week mostly sucked, no thanks to the two companies above. Last month Sky announced that they were launching a new &lt;span class="caps"&gt;ADSL&lt;/span&gt; service called Sky Broadband. They were offering up to 16Mb with no caps for only Â£10 per month for Sky TV subscribers. My previous &lt;span class="caps"&gt;ISP&lt;/span&gt;, &lt;a href="http://zeninternet.co.uk/"&gt;Zen Internet&lt;/a&gt; were good with decent support, but they were not cheap: Â£25 per month for up to 8Mb (I never got more than 3Mb) and a 20GB cap is not what I call good value for money. How could I not jump at the offer?&lt;/p&gt;
&lt;p&gt;It turns out that it was the worst decision I&amp;#8217;ve made in a while. Through a combination of Sky&amp;#8217;s complete and utter incompetence and lack of communication and what I can only assume is laziness and more incompetence on BT&amp;#8217;s part, I have been without broadband net access at home since September 15th, which is when my Zen connection was cancelled. To make matters worse, we just moved offices here at Agile Evolved and we didn&amp;#8217;t get broadband installed until September 21st &amp;#8211; nearly a week without any net access! My Sky connection should have been activated on the 11th but it wasn&amp;#8217;t. With RailsConf coming up I decided to wait until the following Monday to chase things up, and so began a week of constantly calling Sky (with up to half hour hold times) trying to find out what on earth was going on, only to be told that &amp;#8220;there was a delay at BT&amp;#8217;s end&amp;#8221;. You see, because Sky is a &lt;span class="caps"&gt;LLU&lt;/span&gt; (local loop unbundled) service, BT, who run the phone exchange have to physically connect you to Sky&amp;#8217;s hardware. It never happened. At one point I was told there would be up to a 30 day delay. That&amp;#8217;s just not acceptable.&lt;/p&gt;
&lt;p&gt;Sky must have known that demand would be massive so I don&amp;#8217;t buy any excuses of &amp;#8220;underestimating the demand&amp;#8221;. Nor do I buy the complete passing of the buck to BT &amp;#8211; Sky have to take responsibility too as they are the ones selling the services whilst BT struggle to cope.&lt;/p&gt;
&lt;p&gt;Things can&amp;#8217;t get any worse you might assume, but after cancelling my order yesterday, I rang up to confirm with Sky that it had been cancelled. I was told that it has and that the cancellation had been sent through to BT. Unfortunately the order has left what is known as a &amp;#8220;marker&amp;#8221; on my line which prevents me from ordering broadband from anybody else until it is removed. I rang up BT&amp;#8217;s wholesale division to have it removed and even though the lady was very helpful she told me that it my line was showing up as having an &amp;#8220;incompatible product&amp;#8221; and that it could take up to two weeks to have the marker removed. She promised to put it through as urgent and said that it could be done by next Monday, even tomorrow but the possibility remains that I will be without net access at home for another two weeks.&lt;/p&gt;
&lt;p&gt;The bottom line&amp;#8230;if you are in the UK and were thinking about getting Sky Broadband &amp;#8211; don&amp;#8217;t bother. At least, not for now. Wait until things are settled down a bit &amp;#8211; I &lt;strong&gt;might&lt;/strong&gt; be willing to give them another chance in 3 to 6 months but I seriously advice people to give them a miss for now unless you have some kind of backup, like a cable broadband connection that you can leave active until your Sky Broadband connection is up and running. If only I could get cable where I live, as I used Telewest cable when I lived in Wolverhampton and I never had a problem.&lt;/p&gt;
&lt;p&gt;And breathe&amp;#8230;&lt;/p&gt;
&lt;p&gt;If there was one good thing that came out of my downtime last week it was that I was able to spend some time developing my Ruby Specifications library, ActiveSpec. More on that soon&amp;#8230;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=4icQoUlyO_Y:vA65mcGSu3w:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=4icQoUlyO_Y:vA65mcGSu3w:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=4icQoUlyO_Y:vA65mcGSu3w:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=4icQoUlyO_Y:vA65mcGSu3w:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=4icQoUlyO_Y:vA65mcGSu3w:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=4icQoUlyO_Y:vA65mcGSu3w:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=4icQoUlyO_Y:vA65mcGSu3w:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=4icQoUlyO_Y:vA65mcGSu3w:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/4icQoUlyO_Y" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/give-me-my-internet-back.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/ujs-railsconf-europe-2006.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/AmmpUhGwiQU/ujs-railsconf-europe-2006.html" rel="alternate" />
    <published>2006-09-15T13:35:00Z</published>
    <updated>2009-02-15T10:04:52Z</updated>
    <title>UJS @ RailsConf Europe 2006</title>
    <content type="html">&lt;div class="notice"&gt;Update 15 Feb 2009: The &lt;span class="caps"&gt;UJS&lt;/span&gt; plugin for Rails is no longer actively maintained but you can find the &lt;a href="http://github.com/lukeredpath/ujs4rails"&gt;the source on github&lt;/a&gt;.&lt;/div&gt;
&lt;p&gt;There has been a tremendous amount of interest at RailsConf Europe in the &lt;a href="http://www.ujs4rails.com"&gt;&lt;span class="caps"&gt;UJS&lt;/span&gt; Rails Plugin&lt;/a&gt;. &lt;a href="http://www.danwebb.net"&gt;Dan&lt;/a&gt; gave his talk on Thursday morning and it was a struggle to get in; it was completely oversubscribed (I had to use the old &amp;#8216;but I&amp;#8217;m the co-author!&amp;#8217; line just to get in). It was great to see the interest it had generated. The talk proved so popular that a second presentation was scheduled on the Friday in the main congress hall which again proved really popular; we managed to fill up most of the room despite it being lunch &amp;#8211; not a bad achievement.&lt;/p&gt;
&lt;p&gt;Dan gave a great presentation that went into the full details of the whys and wheres of unobtrusive scripting before giving a run down of the core &lt;span class="caps"&gt;UJS&lt;/span&gt; features, wrapping up with a small case study: building an &lt;span class="caps"&gt;AJAX&lt;/span&gt;-powered shopping cart using &lt;span class="caps"&gt;UJS&lt;/span&gt;. He showed how to build the shopping cart so it works in a traditional manner without JavaScript and then he used &lt;span class="caps"&gt;UJS&lt;/span&gt; to layer the additional behaviour on top of that.&lt;/p&gt;
&lt;p&gt;&lt;img src="/assets/2006/9/15/dan-ujs-railsconf-mainhall.jpg" alt="RailsConf Photo" title="Dan presenting UJS at RailsConf Europe 2006" /&gt;&lt;/p&gt;
&lt;p&gt;Some people brought up some interesting questions at the end of the talk, some of which I&amp;#8217;d like to address.&lt;/p&gt;
&lt;p&gt;One person asked if there were any major drawbacks to working this way. In general, I think you&amp;#8217;ll find that once you have got into the right mindset, you will find yourself always thinking about how things will work with and without JavaScript. Its important to stress that it &lt;strong&gt;is&lt;/strong&gt; a mindset change, as Dan pointed out in his presentation. It took some time to convince people of the advantages of &lt;span class="caps"&gt;CSS&lt;/span&gt;-powered layouts and I have a feeling that it might take some time with unobtrusive scripting but I think we&amp;#8217;ll get there eventually.&lt;/p&gt;
&lt;p&gt;Somebody questioned whether or not this approach will work when you trying to create a web-based application that mimcs a desktop application; he cited something like Google Spreadsheets or Google Maps. I think in situations like this it could be difficult to use &lt;span class="caps"&gt;UJS&lt;/span&gt;, simply because creating a fallback would be difficult. You&amp;#8217;d also have to question the usefulness of an application like a spreadsheet that didn&amp;#8217;t work without JavaScript &amp;#8211; how &lt;em&gt;usable&lt;/em&gt; would it be? Campfire is another example of where the &lt;span class="caps"&gt;AJAX&lt;/span&gt; functionality is integral to the way in which it works.&lt;/p&gt;
&lt;p&gt;Google Maps on the other hand could easily be made unobtrusive. Think about it: how did web-based mapping apps work before &lt;span class="caps"&gt;AJAX&lt;/span&gt; came along? They presented a static map with arrows on each side for moving across the map. It is slow, but it works. There is no reason why Google Maps can&amp;#8217;t fall back on this behaviour in the absence of JavaScript.&lt;/p&gt;
&lt;p&gt;On a related note, it was great to hear from the guys at &lt;a href="http://www.odeo.com"&gt;Odeo&lt;/a&gt; and that they really liked the &lt;span class="caps"&gt;UJS&lt;/span&gt; plugin. They mentioned that most of Odeo is unobtrusive and they had to roll a lot of their own JavaScript helpers and raw JavaScript to achieve this. If Odeo start using &lt;span class="caps"&gt;UJS&lt;/span&gt;, that would just be awesome.&lt;/p&gt;
&lt;p&gt;There was one other concern relating to the use of links for &lt;span class="caps"&gt;GET&lt;/span&gt; requests only (with no side-effects) and buttons for everything else (&lt;span class="caps"&gt;POST&lt;/span&gt;, &lt;span class="caps"&gt;PUT&lt;/span&gt; and &lt;span class="caps"&gt;DELETE&lt;/span&gt; in &lt;span class="caps"&gt;REST&lt;/span&gt; terms). The concern revolved around the ability to style buttons. Styling buttons can be tricky in some browsers (such as Camino) but this is easily worked around with a bit of clever &lt;span class="caps"&gt;CSS&lt;/span&gt; or use of an image button.&lt;/p&gt;
&lt;p&gt;One thing Dan didn&amp;#8217;t mention in his presentation, which is worth pointing out, is that the &lt;span class="caps"&gt;UJS&lt;/span&gt; plugin upgrades the built-in Rails helpers (&lt;code class="ruby"&gt;link_to_remote&lt;/code&gt; are given the appropriate values.&lt;/p&gt;
&lt;p&gt;However, I still want to stress that if you can use the &lt;code&gt;link_to_remote&lt;/code&gt;&lt;/pre&gt; can make things a bit funky:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;&amp;lt;% @products.each do |p| %&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;%= link_to_remote product.description, :controller =&amp;gt; 'products', 
                                                :action =&amp;gt; 'description', 
                                                :id =&amp;gt; p.id %&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;% end %&amp;gt;&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;The above will work, but &lt;em&gt;very&lt;/em&gt; slowly. That&amp;#8217;s one behaviour rule for &lt;em&gt;every&lt;/em&gt; &lt;code&gt;div&lt;/code&gt;&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;&amp;lt;div id="products"&amp;gt;
&amp;lt;% @products.each do |p| %&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;%= link_to product.description,
           :controller =&amp;gt; 'products',
           :action =&amp;gt; 'description', 
           :id =&amp;gt; p.id %&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;% end %&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;

&lt;code class="ruby"&gt;&amp;lt;% apply_behaviour "#products a", make_remote_link %&amp;gt;&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;One behaviour for any number of products.&lt;/p&gt;
&lt;p&gt;If you haven&amp;#8217;t tried &lt;span class="caps"&gt;UJS&lt;/span&gt; yet, do give it a go. &lt;a href="http://www.ujs4rails.com"&gt;www.ujs4rails.com&lt;/a&gt; is the official website, and it contains documentation, tips, tricks, a quick start guide and things such as rdocs and a Trac for filing tickets.&lt;/p&gt;
&lt;p&gt;If you like to live on the edge, feel free to try running on the trunk &amp;#8211; we have some cool new ideas on the way for 0.4 and 0.5, including custom assertions for Test::Unit for asserting behaviour (and some extensions for RSpec too!) and a cool in-browser debugger that will let you do things such as view elements with behaviour applied and what behaviours are applied to those elements. Combined with &lt;a href="https://addons.mozilla.org/firefox/1843/"&gt;FireBug&lt;/a&gt; and Thomas Fuchs&amp;#8217; unittest.js library (&lt;a href="http://lukeredpath.co.uk/2006/9/14/thomas-fuchs-adventures-in-javascript-testing"&gt;see my article on his RailsConf talk&lt;/a&gt;), that should make a pretty awesome testing environment for your &lt;span class="caps"&gt;AJAX&lt;/span&gt; apps.&lt;/p&gt;
&lt;p&gt;Have fun with &lt;span class="caps"&gt;UJS&lt;/span&gt;. All feedback is welcome, especially if you have some ideas that you think would make &lt;span class="caps"&gt;UJS&lt;/span&gt; even cooler.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=AmmpUhGwiQU:RD931WK4nps:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=AmmpUhGwiQU:RD931WK4nps:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=AmmpUhGwiQU:RD931WK4nps:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=AmmpUhGwiQU:RD931WK4nps:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=AmmpUhGwiQU:RD931WK4nps:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=AmmpUhGwiQU:RD931WK4nps:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=AmmpUhGwiQU:RD931WK4nps:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=AmmpUhGwiQU:RD931WK4nps:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/AmmpUhGwiQU" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/ujs-railsconf-europe-2006.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/thomas-fuchs-adventures-in-javascript-testing.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/Uako5OrnQ1Q/thomas-fuchs-adventures-in-javascript-testing.html" rel="alternate" />
    <published>2006-09-14T15:58:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>RailsConf: Adventures in JavaScript testing by Thomas Fuchs</title>
    <content type="html">&lt;p&gt;I&amp;#8217;m currently sitting in the main congress hall awaiting the start of Thomas Fuch&amp;#8217;s presentation. As I&amp;#8217;m sure many readers of my blog are aware, I&amp;#8217;m a huge advocate of testing, or to be specific, &lt;strong&gt;good&lt;/strong&gt; testing. However, one thing that I&amp;#8217;ve never managed to get into is unit testing with JavaScript. I&amp;#8217;ve never really been aware of the tools available or how well it works.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m going to write this post live; hopefully it won&amp;#8217;t just come across as a mish-mash of incoherent ramblings.&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Traditional JavaScript &amp;#8220;testing&amp;#8221; generally revolves around &lt;code&gt;alert&lt;/code&gt;. it can be useful sometimes but it generally doesn&amp;#8217;t offer anything interesting. Use it sparingly.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;There are many common misconceptions or attitudes towards JavaScript: &amp;#8220;we don&amp;#8217;t need tests for 10 lines of code&amp;#8221;; &amp;#8220;but you can&amp;#8217;t debug JavaScript&amp;#8221;. Wrong and wrong again. Enter unit testing for JavaScript.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;Some options exist already, such as &lt;a href="http://www.jsunit.net/"&gt;jsunit&lt;/a&gt;. These have their problems, such as awkward syntax or problems with Prototype.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://script.aculo.us/"&gt;Scriptaculous&lt;/a&gt; has its own unittest.js library. All you need to do is include the unittest.js file and an empty &lt;code&gt;div&lt;/code&gt; with a &amp;#8220;testlog&amp;#8221; &lt;span class="caps"&gt;HTML&lt;/span&gt; ID (defaults). Now you&amp;#8217;re ready to roll with Test.Unit.Runner.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;Test.Unit.Runner looks quite similar to the standard xUnit family of testing libraries. It has the standard setup and teardown methods and a suite of assertions.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;It has some good testing functionality for dealing with effects, and time-sensitive behaviour (wait function).&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;It has a basic benchmarking system too!&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;Rake integration is provided through a the &lt;a href="http://dev.rubyonrails.org/svn/rails/plugins/javascript_test/"&gt;javascript_test plugin&lt;/a&gt; &amp;#8211; it allows you to launch browsers and run your javascript unit tests through a single command: &lt;code&gt;rake test:javascripts&lt;/code&gt;. It uses a WEBrick server and supports multiple browsers, including Camino, Firefox, Safari and Internet Explorer. It&amp;#8217;s fairly trivial to add support for other browsers too. It seems to be missing the functionality to close the browser after the rake test run. Perhaps the browser definition classes could be extended to support a &amp;#8220;close&amp;#8221; method that is called after the test run?&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;The plugin also provides a generator for generating test stub files (&lt;span class="caps"&gt;HTML&lt;/span&gt; format) in test/javascript.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;Did I mention the browser-based test runner looks pretty sweet?&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;OK, here&amp;#8217;s the killer feature so far: RSpec-style syntax, including should* style expectations and context/spec format. I&amp;#8217;m convinced, sign me up!&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;Some other hints and tips: If you aren&amp;#8217;t using Firebug already, then start using it! It is awesome and makes debugging &lt;span class="caps"&gt;AJAX&lt;/span&gt; and general JavaScript problems a lot easier. (as an aside, its also great for debugging your behaviours when you are using &lt;a href="http://www.ujs4rails.com"&gt;&lt;span class="caps"&gt;UJS&lt;/span&gt;&lt;/a&gt;). There is also a tool for Safari called &lt;a href="http://webkit.org/blog/?p=61"&gt;Drosera&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;Don&amp;#8217;t forget your &lt;span class="caps"&gt;RJS&lt;/span&gt; templates either. &lt;span class="caps"&gt;RJS&lt;/span&gt; has some basic debugging support built-in (although this could really do with being expanded upon).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;During the question and answer session, &lt;a href="http://simon.incutio.com"&gt;Simon Willison&lt;/a&gt; mentioned a tool that was part of the &lt;a href="http://dojotoolkit.org/"&gt;Dojo Toolkit&lt;/a&gt; that uses a command-line JavaScript interpreter to run its unit tests. It sounds interesting although I feel its a lot more useful to run the tests in the actual environment they are going to run in (i.e. the browsers). By introducing something like a separate interpreter that end-users aren&amp;#8217;t going to be using, you are relying on that interpreter being correct.&lt;/p&gt;
&lt;p&gt;All in all, it was a good talk and has really got me interested in unit testing with JavaScript. It&amp;#8217;s definately something I should be doing, especially as I am so strict about testing with the rest of my code.&lt;/p&gt;
&lt;p&gt;Finally, a word of warning for those who want to try a bit of live blogging: &lt;span class="caps"&gt;IRC&lt;/span&gt; is evil and distracting. Use with caution!&lt;/p&gt;
&lt;h3&gt;Links and useful information:&lt;/h3&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://script.aculo.us"&gt;Scriptaculous&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;#prototype &lt;span class="caps"&gt;IRC&lt;/span&gt; on Freenode.&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://groups.google.com/group/rubyonrails-spinoffs"&gt;Rails Spinoffs Google Group&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=Uako5OrnQ1Q:OSBaaLCYCZM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=Uako5OrnQ1Q:OSBaaLCYCZM:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=Uako5OrnQ1Q:OSBaaLCYCZM:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=Uako5OrnQ1Q:OSBaaLCYCZM:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=Uako5OrnQ1Q:OSBaaLCYCZM:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=Uako5OrnQ1Q:OSBaaLCYCZM:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=Uako5OrnQ1Q:OSBaaLCYCZM:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=Uako5OrnQ1Q:OSBaaLCYCZM:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/Uako5OrnQ1Q" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/thomas-fuchs-adventures-in-javascript-testing.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/new-business-cards.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/gw9qty4N298/new-business-cards.html" rel="alternate" />
    <published>2006-09-08T12:15:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>New business cards</title>
    <content type="html">&lt;p&gt;Check out our new &lt;a href="http://www.agileevolved.com"&gt;Agile Evolved&lt;/a&gt; business cards which arrived today, just in time for RailsConf.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.flickr.com/photos/lukeredpath/237748463/" title="Photo Sharing"&gt;&lt;img src="http://static.flickr.com/82/237748463_d48630af72_o.jpg" width="680" height="313" alt="businesscards" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;They look really cool. They were printed by &lt;a href="http://aubergineprint.co.uk/"&gt;Aubergine Print&lt;/a&gt;. Our &lt;a href="http://lukeredpath.co.uk/assets/2006/9/8/agileevolved_logo.jpg"&gt;new logo&lt;/a&gt; was designed by &lt;a href="http://www.tokyocube.com/"&gt;Tokyo Cube&lt;/a&gt;. Mine haven&amp;#8217;t arrived yet, but when they do I&amp;#8217;ll update the above photo with something a bit more high-quality (theres only so much you can do in Photoshop to improve a camera phone picture).&lt;/p&gt;
&lt;p&gt;There will hopefully be many of these being handed out at next week&amp;#8217;s &lt;a href="http://europe.railsconf.org/"&gt;European RailsConf&lt;/a&gt;.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=gw9qty4N298:YATE3w6Ld-Y:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=gw9qty4N298:YATE3w6Ld-Y:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=gw9qty4N298:YATE3w6Ld-Y:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=gw9qty4N298:YATE3w6Ld-Y:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=gw9qty4N298:YATE3w6Ld-Y:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=gw9qty4N298:YATE3w6Ld-Y:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=gw9qty4N298:YATE3w6Ld-Y:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=gw9qty4N298:YATE3w6Ld-Y:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/gw9qty4N298" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/new-business-cards.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/decorator-pattern-with-ruby-in-8-lines.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/yr_CMQKZarc/decorator-pattern-with-ruby-in-8-lines.html" rel="alternate" />
    <published>2006-09-06T19:30:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>Decorator Pattern with Ruby in 8 lines</title>
    <content type="html">&lt;p&gt;The &lt;a href="http://en.wikipedia.org/wiki/Decorator_pattern"&gt;Decorator Pattern&lt;/a&gt; is a design pattern that enables you to dynamically wrap behaviour around an existing object at runtime. It is especially useful when an object can have many variables that can be combined in different ways, which in turn affect it&amp;#8217;s behaviour.&lt;/p&gt;
&lt;p&gt;This small, simple implementation of the decorator pattern in Ruby sums up what I love about the hottest dynamic language on the planet.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ll borrow a simple example from the excellent &lt;a href="http://www.oreilly.com/catalog/hfdesignpat/"&gt;Head First Design Patterns&lt;/a&gt; book by Eric Freeman, Elisabeth Freeman, Kathy Sierra, and Bert Bates.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s say you want to calculate the cost of a cup of coffee. You have Coffee class, which implements a &lt;code&gt;cost()&lt;/code&gt;&lt;/pre&gt; method. For the purposes of this example, let&amp;#8217;s just hardcode a value:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;class Coffee
  def cost
    2
  end
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;Great. But what if we want the cost of a coffee with milk? We could have a new class:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;class WhiteCoffee
  def cost
    2.4
  end
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;OK. But now we want cream. And sprinkles. Clearly, creating new classes is going to lead to a huge number of classes in our application. It just isn&amp;#8217;t realistic to create classes for different combinations of coffee and extras. It could get worse &amp;#8211; what if we have different types of coffee? We would then have to have combinations of extras with each different type of coffee. It just isn&amp;#8217;t going to work. Enter the decorator pattern. Here&amp;#8217;s the the 8 lines of Ruby mentioned in this entry&amp;#8217;s title:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;module Decorator
  def initialize(decorated)
    @decorated = decorated
  end&lt;/code&gt;

&lt;code class="ruby"&gt;  def method_missing(method, *args)
    args.empty? ? @decorated.send(method) : @decorated.send(method, args)
  end
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;And thats all you need. You can include this into any class you want to act as a decorator. You can then use that decorator as if it was the object it is decorating; by default all messages sent to the decorator are forwarded on to the decorated object. You can then decorate the methods you need to extend:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;class Milk
  include Decorator&lt;/code&gt;

&lt;code class="ruby"&gt;  def cost
    @decorated.cost + 0.4
  end
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;So how does this solve our original problem? The real power of decorators lies in the fact that they can act like the objects they are decorating. By taking this one step further, you can wrap decorators with other decorators as long as they share the same interface. By creating decorators for our different &amp;#8220;extras&amp;#8221;, we can create coffees using a combination of decorators and get the total cost of the coffee.&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;class Whip
  include Decorator&lt;/code&gt;

&lt;code class="ruby"&gt;  def cost 
    @decorated.cost + 0.2
  end
end&lt;/code&gt;

&lt;code class="ruby"&gt;class Sprinkles
  include Decorator&lt;/code&gt;

&lt;code class="ruby"&gt;  def cost
    @decorated.cost + 0.3
  end
end&lt;/code&gt;

&lt;code class="ruby"&gt;Whip.new(Coffee.new).cost
#=&amp;gt; 2.2
Sprinkles.new(Whip.new(Milk.new(Coffee.new))).cost
#=&amp;gt; 2.9&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;Of course, there&amp;#8217;s nothing stopping us from making life easier with a few factory methods:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;class CoffeeFactory
  def self.latte
    SteamedMilk.new(Espresso.new)
  end
  
  def self.cappuccino
    Sprinkles.new(Cream.new(Milk.new(Coffee.new)))
  end
end&lt;/code&gt;

&lt;code class="ruby"&gt;order = Order.new
order.add(Coffee.new)
order.add(CoffeeFactory.cappuccino)
puts order.total&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;Due to Ruby&amp;#8217;s highly dynamic nature, the Decorator pattern isn&amp;#8217;t the only way of extending class functionality at runtime, however I just love how simple it is to implement the pattern in Ruby. For more information on implementing decorators in Ruby, including generic decorators and alternatives to the traditional decorator pattern, see the &lt;a href="http://wiki.rubygarden.org/Ruby/page/show/DecoratorPattern"&gt;DecoratorPattern page at the RubyGarden&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Finally, it would be nice to maintain some kind of identity when using decorators. The RubyGarden page above has one approach. Whilst we aren&amp;#8217;t using inheritance, it would be nice to retain one of its features:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;CoffeeFactory.cappucino.kind_of? Coffee
#=&amp;gt; true&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;I can think of a few ways of extending my Decorator class to retain this functionality, but I&amp;#8217;m gonna leave this one to the reader. Over to you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: In typical Ruby-fashion, my 8 lines of code has been beaten by, well, zero lines of code by &lt;a href="http://somethinglearned.com"&gt;Trevor Squires&lt;/a&gt;. Using modules, super and extend, Trevor came up with this &lt;a href="http://pastie.caboo.se/12066"&gt;alternative solution&lt;/a&gt;, which maintains identity as well. Bow down to his Ruby-fu.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update 2&lt;/strong&gt;: Never one to step back from a challenge, I see Trevor&amp;#8217;s Ruby-fu and raise him with a bit of my own. I still feel my decorator implementation carries some weight, with a nice bit of syntatic sugar:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;class Milk
  include Decorator
end&lt;/code&gt;

&lt;code class="ruby"&gt;class Whip
  include Decorator
end&lt;/code&gt;

&lt;code class="ruby"&gt;class Sprinkles
  include Decorator
end&lt;/code&gt;

&lt;code class="ruby"&gt;# normal coffee
Coffee.new&lt;/code&gt;

&lt;code class="ruby"&gt;# coffee with milk, whip and sprinkles
Coffee.with :milk, :whip, :sprinkles&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;self.with&lt;/code&gt;&lt;/pre&gt; method could do with being extracted into a Decoratable module but &lt;a href="http://pastie.caboo.se/12092"&gt;here&amp;#8217;s a concrete implementation&lt;/a&gt;. Trevor&amp;#8217;s method still has an advantage in that it maintains object identity, but I have some ideas to solve that. But alas, it is past 1am and bed beckons.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update 3&lt;/strong&gt;: Trevor fights back with &lt;a href="http://pastie.caboo.se/12097"&gt;his own self.with() implementation&lt;/a&gt;. Personally I prefer the &lt;code&gt;extend&lt;/code&gt;&lt;/pre&gt; implementation, it eliminates the symbol to class trickery, and still maintains identity. His &amp;#8217;fu is still strong.&lt;/p&gt;
&lt;p&gt;However, there is one downside to Trevor&amp;#8217;s method: you can only apply a decorator to an object using &lt;filterjscode lang="ruby" inline="true"&gt;extend&lt;/code&gt;&lt;/pre&gt; once. But what if you wanted double sprinkles?&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;Sprinkles.new(Sprinkles.new(Coffee.new))&lt;/code&gt;

&lt;code class="ruby"&gt;# or with a bit of sugar (no pun intended)
Coffee.with :sprinkles, :sprinkles
&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=yr_CMQKZarc:s_FqZnkWLwA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=yr_CMQKZarc:s_FqZnkWLwA:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=yr_CMQKZarc:s_FqZnkWLwA:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=yr_CMQKZarc:s_FqZnkWLwA:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=yr_CMQKZarc:s_FqZnkWLwA:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=yr_CMQKZarc:s_FqZnkWLwA:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=yr_CMQKZarc:s_FqZnkWLwA:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=yr_CMQKZarc:s_FqZnkWLwA:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/yr_CMQKZarc" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/decorator-pattern-with-ruby-in-8-lines.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/the-first-rule-of-barcamp.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/2W7-avaV688/the-first-rule-of-barcamp.html" rel="alternate" />
    <published>2006-09-02T14:55:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>The first rule of Barcamp...</title>
    <content type="html">&lt;p&gt;Ongoing updates live (or when I get round to it) from Barcamp London. I&amp;#8217;ll be posting notes on the presentations I attend as the weekend goes on.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://www.lukeredpath.co.uk/assets/2006/9/2/barcamp-yahoo.jpg" alt="Yahoo reception photo" title="Sitting in Yahoo's reception at Barcamp London" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4pm&lt;/strong&gt;: Above is my current view as I sit in the reception of Yahoo&amp;#8217;s swanky West End offices, trying to polish off the second part of my &lt;span class="caps"&gt;BDD&lt;/span&gt;/Rails tutorial, whilst panicking about my &lt;span class="caps"&gt;UJS&lt;/span&gt; presentation tomorrow (&lt;span class="caps"&gt;AKA&lt;/span&gt; my first ever presentation).&lt;/p&gt;
&lt;p&gt;After arriving fashionably late (ok, 4 hours late) I dropped into &lt;a href="http://noodlesinmysandles.com"&gt;Jonathan&amp;#8217;s&lt;/a&gt; Comet talk, which was followed up by a talk on the Low Pro extension to the &lt;a href="http://prototype.conio.net"&gt;Prototype&lt;/a&gt; Javascript library by &lt;a href="http://www.danwebb.net"&gt;Dan Web&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Jonathan&amp;#8217;s talk on Comet gave me some ideas about a forthcoming project we&amp;#8217;re gonna be working on here at Agile Evolved. There do seem to be a few implementation issues to resolve first however.&lt;/p&gt;
&lt;p&gt;Surprisingly, Dan&amp;#8217;s &lt;a href="http://projects.danwebb.net/wiki/LowPro"&gt;Low Pro&lt;/a&gt; talk was a revelation to me. I say surprsing, because you think I&amp;#8217;d know all about Low Pro, what with it being the core behind the &lt;a href="http://www.ujs4rails.com"&gt;&lt;span class="caps"&gt;UJS&lt;/span&gt; For Rails&lt;/a&gt; plugin however I was only really aware of its improved event handling capabilities. Even cooler was its improved &lt;span class="caps"&gt;DOM&lt;/span&gt; support and the real killer for me, Behaviour classes. Behaviour classes let you build self-contained classes that contain behaviour for a collection of events, which can then be applied to elements in one goal. Dan is going to be blogging about this in full, but here is a small snippet:&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;var Sortable = Behaviour.create({
  initialize : function() { },
  onclick: function() { },
  onmouseup: function() { }
});&lt;/code&gt;

&lt;code class="javascript"&gt;Event.addBehaviour("#todolist li", Sortable);&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;How awesome is that? It would be a great to see a re-implementation of script.aculo.us behaviours using Behaviour classes. Its also a great way of building and packaing up your own unique behaviours.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6:14pm&lt;/strong&gt;: Award for most bizarre use of the English language goes to &lt;a href="http://simon.incutio.com"&gt;Simon Willison&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote cite="Simon Willison, Barcamp Longon"&gt;
&lt;p&gt;&amp;#8220;I&amp;#8217;m still upset on a theoretical level.&amp;#8221;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Sunday, 3pm&lt;/strong&gt;: OK, here&amp;#8217;s some advice. If you are going to do a presentation, don&amp;#8217;t leave it until the last minute to put together. I managed to get my slides finished by about half past 1 and was on the train into central London by 2. In the end my presentation seemed to go ok, though I&amp;#8217;m still not a fan of public speaking.&lt;/p&gt;
&lt;p&gt;Despite arriving so late, I did manage to couple catch a couple of interesting talks. &lt;a href="http://www.nickfitz.co.uk/"&gt;Nick Fitzsimons&lt;/a&gt; gave an interesting talk on the correct use of &lt;span class="caps"&gt;HTTP&lt;/span&gt;, which extended nicely from a talk on Saturday regarding &lt;span class="caps"&gt;REST&lt;/span&gt; and Rails (and whether or not to use it). We generally agreed that in an ideal world, content negotiation and a single &lt;span class="caps"&gt;URL&lt;/span&gt; for a resource (with the accept header determining how you want that resource to be presented) is a good thing. However, there were concerns voiced about misconfigured proxy servers that could end up munging with accept headers which would result in some very strange behaviour.&lt;/p&gt;
&lt;p&gt;People were more positive about using &lt;span class="caps"&gt;HTTP&lt;/span&gt; verbs to perform different actions on a resource. The issue surrounding proxy servers (which may not allow certain verbs) remained but it was less severe as a proxy server that did not support a particular verb would simply fail, rather than appear to work but actually do something strange.&lt;/p&gt;
&lt;p&gt;Thanks to those who did attend my talk on Unobtrusive Javascript with Ruby on Rails. If you weren&amp;#8217;t able to attend Barcamp, you can &lt;a href="http://www.ujs4rails.com/assets/2006/9/4/BarCamp_London_UJS_Presentation.pdf"&gt;download the slides&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;All in all it was a good weekend. It was good to meet some interesting and friendly people. If you haven&amp;#8217;t attended a Barcamp before, I suggest you keep an eye out for one in your area, as they are great fun.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=2W7-avaV688:XLsGgWQL5no:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=2W7-avaV688:XLsGgWQL5no:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=2W7-avaV688:XLsGgWQL5no:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=2W7-avaV688:XLsGgWQL5no:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=2W7-avaV688:XLsGgWQL5no:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=2W7-avaV688:XLsGgWQL5no:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=2W7-avaV688:XLsGgWQL5no:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=2W7-avaV688:XLsGgWQL5no:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/2W7-avaV688" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/the-first-rule-of-barcamp.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/developing-a-rails-model-using-bdd-and-rspec-part-1.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/ig8r36QiWd4/developing-a-rails-model-using-bdd-and-rspec-part-1.html" rel="alternate" />
    <published>2006-08-29T13:15:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>Developing a Rails model using BDD and RSpec, Part 1</title>
    <content type="html">&lt;p&gt;Writing Rails testing articles seems to be quite popular at the moment; seeing as I&amp;#8217;m often quite vocal about testing on the #caboose and #rubyonrails &lt;span class="caps"&gt;IRC&lt;/span&gt; rooms I felt it was about time I posted one of my own. I have a large series of articles on testing with Rails in the pipeline, but until that is done, here is a nice and simple tutorial for newcomers to &lt;span class="caps"&gt;BDD&lt;/span&gt; and RSpec &amp;#8211; the first in a two-part article exploring the development of a typical Rails model, using &lt;span class="caps"&gt;BDD&lt;/span&gt; techniques and the RSpec framework. If you are interested in &lt;span class="caps"&gt;BDD&lt;/span&gt; and RSpec, or new to testing in general and want to learn how to iteratively develop a model test/spec-first, this is the article for you.&lt;/p&gt;
&lt;h3&gt;Anatomy of a typical Rails-style test&lt;/h3&gt;
&lt;p&gt;Will Rails developers please raise their hands: how many times have you written a test that looks like this:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;class UserTest &amp;lt; Test::Unit::TestCase
  def test_create
    user = User.create(:some =&amp;gt; 'params')
    assert user.save
  end
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;Now ask yourself how many times have you sat back and asked yourself &lt;em&gt;why&lt;/em&gt; you are writing the above test?&lt;/p&gt;
&lt;p&gt;If the concept of unit testing is new to you, then writing tests at all is a great first step. But its also important to have useful tests. Are your tests &lt;a href="http://www.jvoorhis.org/articles/2006/08/22/is-your-test-suite-valuable"&gt;valuable&lt;/a&gt;? Are your tests &lt;a href="http://www.jvoorhis.org/articles/2006/08/26/is-your-test-suite-acceptable"&gt;acceptable&lt;/a&gt;?&lt;/p&gt;
&lt;h3&gt;Avoid meaningless tests&lt;/h3&gt;
&lt;p&gt;The above test is a good example of a meaningless test. Why is it meaningless? Because you aren&amp;#8217;t testing your own code; you are testing the ActiveRecord library, which is &lt;a href="http://dev.rubyonrails.org/browser/trunk/activerecord/test"&gt;pretty well tested&lt;/a&gt; already. Let&amp;#8217;s take a look at a default Rails model:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;class User &amp;lt; ActiveRecord::Base
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;Just those two lines of code give us a whole load of functionality, all of which is provided by the ActiveRecord library. Its fair to assume that the functionality given by those two lines of code will work. If it doesn&amp;#8217;t then there is either something wrong with your local setup or something fundamentally wrong with ActiveRecord; in either case, your own tests are the last of your problems.&lt;/p&gt;
&lt;h3&gt;Test your own code&lt;/h3&gt;
&lt;p&gt;So if we can safely assume that the built-in ActiveRecord functionality works as advertised, what &lt;em&gt;should&lt;/em&gt; you be testing? The simple answer: test any code that &lt;em&gt;you&lt;/em&gt; write. Anything that gets added to your model needs test coverage. The aim of this tutorial is to place an emphasis on testing the &lt;em&gt;behaviour&lt;/em&gt; of your code in different situations (or contexts). This is the basis of &lt;a href="http://behaviour-driven.org/"&gt;Behaviour Driven Development&lt;/a&gt;, the methodology that I will use in this tutorial to iteratively develop a Rails model, &lt;del&gt;test-first&lt;/del&gt; &lt;ins&gt;spec-first&lt;/ins&gt;.&lt;/p&gt;
&lt;p&gt;For this tutorial, I will be using the excellent &lt;a href="http://rspec.rubyforge.org"&gt;RSpec&lt;/a&gt; framework, but you could easily apply these principles to &lt;span class="caps"&gt;TDD&lt;/span&gt; using Test::Unit. Before we get started, you&amp;#8217;ll need to install RSpec and the RSpec On Rails plugin for your current app:&lt;/p&gt;
&lt;pre class="shell"&gt;&lt;code class="shell"&gt;~/mygreatapp/ $ sudo gem install rspec
~/mygreatapp/ $ ./script/plugin install svn://rubyforge.org/var/svn/rspec/tags/REL_X_Y_Z/vendor/rspec_on_rails/vendor/plugins/rspec
~/mygreatapp/ $ ./script/generate rspec&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;Replace X, Y and Z in the above with the version of RSpec that you are using. If you have any problems, refer to the &lt;a href="http://rspec.rubyforge.org/tools/rails.html"&gt;full instructions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Going into RSpec in full detail is outside of the scope of this article, but it should be pretty clear what is going on &amp;#8211; this is one of RSpec&amp;#8217;s strengths. If you would like to read a more generic RSpec tutorial, the RSpec website has &lt;a href="http://rspec.rubyforge.org/tutorials/index.html"&gt;a great tutorial&lt;/a&gt; to get you started.&lt;/p&gt;
&lt;h3&gt;The problem&lt;/h3&gt;
&lt;p&gt;We&amp;#8217;re in the process of writing our fantastic new Web 2.0 application, and we&amp;#8217;ve decided that we need people to be able to create accounts and log in to the application. We don&amp;#8217;t want to use any of the available Rails authentication plugins; we want to develop our own User model. After a quick whiteboard/&lt;a href="http://en.wikipedia.org/wiki/CRC_Card"&gt;&lt;span class="caps"&gt;CRC&lt;/span&gt;&lt;/a&gt; session, we come up with a few basic specs for our User model:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;A user should have a username that they can log in with&lt;/li&gt;
	&lt;li&gt;A user should have a password between 6 and 12 characters in length&lt;/li&gt;
	&lt;li&gt;A user&amp;#8217;s password should always be encrypted in the database&lt;/li&gt;
	&lt;li&gt;A user should have an email address&lt;/li&gt;
	&lt;li&gt;A user can optionally have a first name, surname and profile/description&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With this in mind, we fire up our &lt;a href="http://macromates.com/"&gt;favourite text editor&lt;/a&gt; and start work on a new Specification.  We use the generator that comes with RSpec on Rails to generate a new model, with an accompanying spec file.&lt;/p&gt;
&lt;pre class="shell"&gt;&lt;code class="shell"&gt;$ ./script/generate rspec_model User&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;This will create a new user.rb file for our model, just like the normal Rails script/generate model command, but it will also create an accompanying spec file in the spec/ directory. If you open up the created user_spec.rb file, you will see a stub context ready and waiting.&lt;/p&gt;
&lt;p&gt;Behaviour Driven Development favours the breaking up of specifications into individual &amp;#8220;contexts&amp;#8221;. A context is an object (or collection of objects, but generally object being specced) in a certain state. As we are going to start our specs from scratch, you can safely remove the stub context in the user_spec.rb file (don&amp;#8217;t remove the require line at the top though!).&lt;/p&gt;
&lt;p&gt;So what is a good starting point? I tend to favour a more generic starting context: &amp;#8220;A user&amp;#8221;. We can use this to specify the behaviour of a user in general.&lt;/p&gt;
&lt;h3&gt;Specifying your model in code&lt;/h3&gt;
&lt;p&gt;Let start with our first specification: a user should have a username that they can log in with. Its fair to assume that the username is &lt;strong&gt;required&lt;/strong&gt; (otherwise they won&amp;#8217;t be able to log in). So what could we specify? How about this:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;context "A user (in general)" do
  setup do
    @user = User.new
  end&lt;/code&gt;

&lt;code class="ruby"&gt;  specify "must have a username" do&lt;/code&gt;

&lt;code class="ruby"&gt;  end
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;That&amp;#8217;s not bad, but it could be better. We&amp;#8217;ve expressed a requirement in our code but we haven&amp;#8217;t said anything about the behaviour of a User object. What about this instead:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;context "A user (in general)" do
  setup do
    @user = User.new
  end&lt;/code&gt;

&lt;code class="ruby"&gt;  specify "should be invalid without a username" do&lt;/code&gt;

&lt;code class="ruby"&gt;  end
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;That&amp;#8217;s better. Not only have we expressed that our user must have a username, but we&amp;#8217;ve also expressed what behaviour should be expected from the User model if it doesn&amp;#8217;t have one; it should be invalid. Let&amp;#8217;s fill this spec in, so we have a failing spec:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;context "A user (in general)" do
  setup do
    @user = User.new
  end&lt;/code&gt;

&lt;code class="ruby"&gt;  specify "should be invalid without a username" do
    @user.should_not_be_valid
    @user.username = 'someusername'
    @user.should_be_valid
  end
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;Now we need to make this pass. The first things we need is our actual User model, and a table in our database. &lt;span class="caps"&gt;BDD&lt;/span&gt; (and &lt;span class="caps"&gt;TDD&lt;/span&gt;) emphasise taking small steps, following the red, green, refactor mantra. However, due to our coupling to the database as a result of using the ActiveRecord pattern, we are going to have to make a slightly larger leap: our users table schema.&lt;/p&gt;
&lt;p&gt;We need to write a migration for our users table, but at this stage we aren&amp;#8217;t certain exactly what columns we need. We could write a migration every time we want to add a column but that would quickly become tedious. Instead, we&amp;#8217;ll make a reasonable guess at our schema based on our written specs &amp;#8211; if we get it wrong at this stage it doesn&amp;#8217;t matter. Migrations make it easy to modify our schema in the future. Something like this should do the trick:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;class AddUsersTable &amp;lt; ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      t.column :first_name, :string
      t.column :last_name, :string
      t.column :email, :string
      t.column :description, :string
      t.column :username, :string
      t.column :encrypted_password, :string
      t.column :salt, :string
    end
  end&lt;/code&gt;

&lt;code class="ruby"&gt;  def self.down
    drop_table :users
  end
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;You&amp;#8217;ll note that we&amp;#8217;ve made a few assumptions regarding our password columns. We already have an idea in mind about how we want to store the password &amp;#8211; as a salted hash &amp;#8211; so we&amp;#8217;ve created columns for the encrypted password and salt. Now we&amp;#8217;ve written and run our migration, and created our User model, its time to get the spec to pass:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;class User &amp;lt; ActiveRecord::Base
  validates_presence_of :username
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;You&amp;#8217;ll notice that we&amp;#8217;ve not added a &lt;em&gt;should&lt;/em&gt; statement for the error message itself. That is because we know Rails will happily provide us with the default &amp;#8220;can&amp;#8217;t be blank&amp;#8221; message. Remember: only test the code that you write. In this case, we decide we &lt;em&gt;do&lt;/em&gt; want a custom message, so lets add a spec and make it pass:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;context "A user (in general)" do
  setup do
    @user = User.new
  end&lt;/code&gt;

&lt;code class="ruby"&gt;  specify "should be invalid without a username" do
    @user.should_not_be_valid
    @user.errors.on(:username).should_equal "is required"
    @user.username = 'someusername'
    @user.should_be_valid
  end
end&lt;/code&gt;

&lt;code class="ruby"&gt;class User &amp;lt; ActiveRecord::Base
  validates_presence_of :username, :message =&amp;gt; 'is required'
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;We&amp;#8217;ve also specified that our user &lt;em&gt;must&lt;/em&gt; have an email address, so lets add a spec for that:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;context "A user (in general)" do
  setup do
    @user = User.new
  end&lt;/code&gt;

&lt;code class="ruby"&gt;  specify "should be invalid without a username" do
    @user.should_not_be_valid
    @user.errors.on(:username).should_equal "is required"
    @user.username = 'someusername'
    @user.should_be_valid
  end&lt;/code&gt;

&lt;code class="ruby"&gt;  specify "should be invalid without an email" do
    @user.should_not_be_valid
    @user.errors.on(:email).should_equal "is required"
    @user.email = 'joe@bloggs.com'
    @user.should_be_valid
  end
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;That&amp;#8217;s simple enough to implement:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;class User &amp;lt; ActiveRecord::Base
  validates_presence_of :username, :message =&amp;gt; 'is required'
  validates_presence_of :email, :message =&amp;gt; 'is required'
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;Great, we&amp;#8217;re on a roll. But wait a minute, both of our specs are now failing. What gives? Of course, because we&amp;#8217;ve now added two validation requirements, we need to add an email address in the first spec to make it pass and a username in the second spec to make that one pass. Hmm, it doesn&amp;#8217;t sound very &lt;span class="caps"&gt;DRY&lt;/span&gt;, but lets go with it for now &amp;#8211; we want our specs to pass after all!&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;context "A user (in general)" do
  setup do
    @user = User.new
  end&lt;/code&gt;

&lt;code class="ruby"&gt;  specify "should be invalid without a username" do
    @user.email = 'joe@bloggs.com'
    @user.should_not_be_valid
    @user.errors.on(:username).should_equal "is required"
    @user.username = 'someusername'
    @user.should_be_valid
  end&lt;/code&gt;

&lt;code class="ruby"&gt;  specify "should be invalid without an email" do
    @user.username = 'joebloggs'
    @user.should_not_be_valid
    @user.errors.on(:email).should_equal "is required"
    @user.email = 'joe@bloggs.com'
    @user.should_be_valid
  end
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;Phew, that was a close one. Finally, lets add the specs for the password. We know a password is required and that it has to be between 6 and 12 characters in length. Because that is actually two specifications, we&amp;#8217;ll write two separate specs in our code. Lets start with the required field specification, as that will look similar to our above specs:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;context "A user (in general)" do
  setup do
    @user = User.new
  end&lt;/code&gt;

&lt;code class="ruby"&gt;  specify "should be invalid without a username" do
    @user.email = 'joe@bloggs.com'
    @user.password = 'abcdefg'
    @user.should_not_be_valid
    @user.errors.on(:username).should_equal "is required"
    @user.username = 'someusername'
    @user.should_be_valid
  end&lt;/code&gt;

&lt;code class="ruby"&gt;  specify "should be invalid without an email" do
    @user.username = 'joebloggs'
    @user.password = 'abcdefg'
    @user.should_not_be_valid
    @user.errors.on(:email).should_equal "is required"
    @user.email = 'joe@bloggs.com'
    @user.should_be_valid
  end&lt;/code&gt;

&lt;code class="ruby"&gt;  specify "should be invalid without a password" do
    @user.email = 'joe@bloggs.com'
    @user.username = 'joebloggs'
    @user.should_not_be_valid
    @user.password = 'abcdefg'
    @user.should_be_valid
  end
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;Now, we don&amp;#8217;t actually have a password column in our users table, but we need somewhere to store the cleartext password before it gets encrypted. A standard Ruby instance variable will do. Here&amp;#8217;s the code to make it pass:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;class User &amp;lt; ActiveRecord::Base
  attr_accessor :password&lt;/code&gt;

&lt;code class="ruby"&gt;  validates_presence_of :username, :message =&amp;gt; 'is required'
  validates_presence_of :email, :message =&amp;gt; 'is required'
  validates_presence_of :password
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;h3. Refactoring towards cleaner, clearer specifications&lt;/p&gt;
&lt;p&gt;Before moving on to the password length specification, lets address our duplication issue here. Its already getting tedious adding all the other required fields in each spec in order to make them pass. It is making our specs bloated, ugly and it will be a nightmare to maintain in the future if our specification changes. Let&amp;#8217;s solve this by introducing a small helper module and a neat &lt;a href="http://pastie.caboo.se/10707"&gt;Hash extension&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;module UserSpecHelper
  def valid_user_attributes
    { :email =&amp;gt; 'joe@bloggs.com',
      :username =&amp;gt; 'joebloggs',
      :password =&amp;gt; 'abcdefg' }
  end
end&lt;/code&gt;

&lt;code class="ruby"&gt;context "A user (in general)" do
  include UserSpecHelper&lt;/code&gt;

&lt;code class="ruby"&gt;  setup do
    @user = User.new
  end&lt;/code&gt;

&lt;code class="ruby"&gt;  specify "should be invalid without a username" do
    @user.attributes = valid_user_attributes.except(:username)
    @user.should_not_be_valid
    @user.errors.on(:username).should_equal "is required"
    @user.username = 'someusername'
    @user.should_be_valid
  end&lt;/code&gt;

&lt;code class="ruby"&gt;  specify "should be invalid without an email" do
    @user.attributes = valid_user_attributes.except(:email)
    @user.should_not_be_valid
    @user.errors.on(:email).should_equal "is required"
    @user.email = 'joe@bloggs.com'
    @user.should_be_valid
  end&lt;/code&gt;

&lt;code class="ruby"&gt;  specify "should be invalid without a password" do
    @user.attributes = valid_user_attributes.except(:password)
    @user.should_not_be_valid
    @user.password = 'abcdefg'
    @user.should_be_valid
  end
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;There, thats much DRYer, more expressive and easier to maintain. If our valid attributes ever change, we only need to change them in one place. However, we haven&amp;#8217;t sacrificed readability in the name of &lt;span class="caps"&gt;DRY&lt;/span&gt;, which is very important with any tests/specs.&lt;/p&gt;
&lt;p&gt;Finally, lets add a spec for our password length:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;specify "should be invalid if password is not between 6 and 12 characters in length" do
  @user.attributes = valid_user_attributes.except(:password)
  @user.password = 'abcdefghijklm'
  @user.should_not_be_valid
  @user.password = 'abcde'
  @user.should_not_be_valid
  @user.password = 'abcdefg'
  @user.should_be_valid
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;And to make it pass:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;class User &amp;lt; ActiveRecord::Base
  attr_accessor :password&lt;/code&gt;

&lt;code class="ruby"&gt;  validates_presence_of :username, :message =&amp;gt; 'is required'
  validates_presence_of :email, :message =&amp;gt; 'is required'
  validates_presence_of :password
  validates_length_of :password, :in =&amp;gt; 6..12, :allow_nil =&amp;gt; :true
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;You&amp;#8217;ll notice we&amp;#8217;ve added the :allow_nil option to the length validation. This is to avoid a double validation error if we haven&amp;#8217;t set a password &amp;#8211; the validates_presence_of validation will already handle this and we don&amp;#8217;t want an extra error message complaining about the length of the password as well.&lt;/p&gt;
&lt;p&gt;There is one last refactoring that we can do at this stage. In each of our validation specs, we&amp;#8217;ve checked that the model is invalid, then set the required value and checked that it is now valid, to ensure that the validation is working end to end. We can extract all of these checks into a single specification:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;specify "should be valid with a full set of valid attributes" do
    @user.attributes = valid_user_attributes
    @user.should_be_valid
  end
&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&lt;/code&gt;

&lt;code class="ruby"&gt;"Download the full specification.":http://pastie.caboo.se/10720/text&lt;/code&gt;

&lt;/pre&gt;
&lt;h3&gt;Whats next?&lt;/h3&gt;
&lt;p&gt;So far we&amp;#8217;ve written a basic User model, with an initial schema and a validation of required attributes. We&amp;#8217;ve covered the basics of RSpec syntax and we&amp;#8217;ve learnt how to &lt;span class="caps"&gt;DRY&lt;/span&gt; up our specs by extracting common code into a helper module.&lt;/p&gt;
&lt;p&gt;In the second part of this tutorial, we&amp;#8217;ll look at password encryption and authentication. If you have any questions or feedback, do not hesitate to leave a comment; I&amp;#8217;ll be happy to answer any queries you may have.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=ig8r36QiWd4:20fMs4sG16Y:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=ig8r36QiWd4:20fMs4sG16Y:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=ig8r36QiWd4:20fMs4sG16Y:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=ig8r36QiWd4:20fMs4sG16Y:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=ig8r36QiWd4:20fMs4sG16Y:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=ig8r36QiWd4:20fMs4sG16Y:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=ig8r36QiWd4:20fMs4sG16Y:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=ig8r36QiWd4:20fMs4sG16Y:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/ig8r36QiWd4" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/developing-a-rails-model-using-bdd-and-rspec-part-1.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/soundbytes-geddit.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/HfVmGqmrmNg/soundbytes-geddit.html" rel="alternate" />
    <published>2006-08-21T18:16:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>SoundBytes: geddit?</title>
    <content type="html">&lt;p&gt;In the style of &lt;a href="http://37signals.com"&gt;37Signals&lt;/a&gt; &amp;#8220;SunSpots&amp;#8221; articles &amp;#8211; a small collection of odds and ends not big enough to warrant an entire entry to themselves &amp;#8211; I present to you SoundBytes; little bites of information (but bytes, because we are geeks, right?) that are, as any of my Northern friends would say, sound.&lt;/p&gt;
&lt;p&gt;Just two small little bite-sized nuggets for this edition&amp;hellip;&lt;/p&gt;
&lt;h3&gt;Automatic stylesheet inclusion with stylesheet_include_tag&lt;/h3&gt;
&lt;p&gt;First up, &lt;code&gt;stylesheet_include_tag&lt;/code&gt;&lt;/pre&gt; is a small Rails helper that I wrote to help me organise my application stylesheets. I named it _include_tag to distinguish it from Rails&amp;#8217; own stylesheet_link_tag.&lt;/p&gt;
&lt;p&gt;First of all, here&amp;#8217;s the code:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;def stylesheet_include_tag(*sources)
  if sources.include?(:controller)
    sources.delete(:controller)
    sources.push(controller_stylesheet_source) if stylesheet_exists(controller_stylesheet_source)
  end
  if sources.include?(:action)
    sources.delete(:action)
    sources.push(action_stylesheet_source) if stylesheet_exists(action_stylesheet_source)
  end
  if sources.include?(:defaults)
    sources.delete(:defaults)
    sources.unshift('application')
    sources.push(controller_stylesheet_source) if stylesheet_exists(controller_stylesheet_source)
    sources.push(action_stylesheet_source) if stylesheet_exists(action_stylesheet_source)
  end
  sources.collect { |source|
    path = "/stylesheets/#{source}.css"
    tag('link', { 'type' =&amp;gt; 'text/css', 'rel' =&amp;gt; 'stylesheet', 'href' =&amp;gt; path})
  }.join("\n")
end&lt;/code&gt;

&lt;code class="ruby"&gt;protected
  def controller_stylesheet_source
    params[:controller]
  end
  
  def action_stylesheet_source
    [ params[:controller], params[:action] ].join("_")
  end
  
  def stylesheet_path(source)
    "#{RAILS_ROOT}/public/stylesheets/#{source}.css"
  end
  
  def stylesheet_exists(source)
    File.exists?(stylesheet_path(source))
  end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;So how do you use this little helper? It&amp;#8217;s quite simple really &amp;#8211; it takes three options:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;code&gt;:controller&lt;/code&gt;&lt;/pre&gt; &amp;#8211; looks for a stylesheet under /public/stylesheets called controllername.css &amp;#8211; if it finds one, it will automatically include it.&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;:action&lt;/code&gt;&lt;/pre&gt; &amp;#8211; as above, except it looks for a stylesheet called controllername_actionname.css&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;:defaults&lt;/code&gt;&lt;/pre&gt; &amp;#8211; a combination of the two above options, plus it also looks for a stylesheet called application.css &amp;#8211; this is the one that I usually use.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So what do you gain? Simply, the ability to break apart your stylesheets into sensible chunks (with global styles in application.css) and not having to worry about adding a link tag for every stylesheet you want to use. Simply drop in a stylesheet under /public/stylesheets using the naming conventions above and it will automatically detect it and add a link tag for you.&lt;/p&gt;
&lt;p&gt;Just drop the above code into your ApplicationHelper (or a separate file, and include it) and that is all there is to it. This is one of the many helpers that I&amp;#8217;ve written that I just couldn&amp;#8217;t live without.&lt;/p&gt;
&lt;h3&gt;Comment notification for Mephisto&lt;/h3&gt;
&lt;p&gt;As you might have read, I recently moved my blog over to Mephisto from Wordpress. I&amp;#8217;m loving Mephisto, but one of the features I miss is email notification of new comments. You could just use the &lt;span class="caps"&gt;RSS&lt;/span&gt; feed but some people, including myself, prefer email notifications. Implementing email notifications was pretty trivial &amp;#8211; it was just a case of hooking up an observer to the Comment class. I&amp;#8217;ve packaged up the extension as a normal Rails plugin. As I&amp;#8217;m using trunk/tags you probably won&amp;#8217;t have much luck installing it with script/plugin, so use svn co instead:&lt;/p&gt;
&lt;pre class="shell"&gt;&lt;code class="shell"&gt;$ svn co http://opensource.agileevolved.com/svn/root/rails_plugins/mephisto_comment_notification/trunk \
  vendor/plugins/mephisto_comment_notification&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;Please report any problems with the above plugin on the &lt;a href="http://opensource.agileevolved.com"&gt;Agile Evolved Open Source Trac&lt;/a&gt;.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=HfVmGqmrmNg:L7jUolS30Vs:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=HfVmGqmrmNg:L7jUolS30Vs:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=HfVmGqmrmNg:L7jUolS30Vs:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=HfVmGqmrmNg:L7jUolS30Vs:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=HfVmGqmrmNg:L7jUolS30Vs:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=HfVmGqmrmNg:L7jUolS30Vs:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=HfVmGqmrmNg:L7jUolS30Vs:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=HfVmGqmrmNg:L7jUolS30Vs:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/HfVmGqmrmNg" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/soundbytes-geddit.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/ujs-rails-plugin-0-3-new-name-new-website.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/YVg7YeGiIgg/ujs-rails-plugin-0-3-new-name-new-website.html" rel="alternate" />
    <published>2006-08-21T13:05:00Z</published>
    <updated>2009-02-15T10:04:52Z</updated>
    <title>UJS Rails Plugin 0.3: new name, new website</title>
    <content type="html">&lt;div class="notice"&gt;Update 15 Feb 2009: The &lt;span class="caps"&gt;UJS&lt;/span&gt; plugin for Rails is no longer actively maintained but you can find the &lt;a href="http://github.com/lukeredpath/ujs4rails"&gt;the source on github&lt;/a&gt;.&lt;/div&gt;
&lt;p&gt;&lt;a href="http://www.danweb.net"&gt;Dan&lt;/a&gt; and I are please to announce the biggest release of the Unobtrusive Javascript for Rails plugin &amp;#8211; now simply known as &lt;span class="caps"&gt;UJS&lt;/span&gt;. This is a big release in many ways &amp;#8211; we&amp;#8217;ve got some important bug fixes in here, some great new helper functionality and most importantly: caching. The other big news is that the &lt;span class="caps"&gt;UJS&lt;/span&gt; plugin now has its &lt;a href="http://www.ujs4rails.com/2006/8/17/welcome-to-the-new-ujs-rails-plugin-website"&gt;very own website&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The website is the place to go for news, information, release announcements (although I will still announce releases on this blog) and tips and tricks &amp;#8211; everything you need to get started with the &lt;span class="caps"&gt;UJS&lt;/span&gt; plugin.&lt;/p&gt;
&lt;p&gt;Before you rush off to check out the new site, here is a quick overview of some of new features:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Added out-of-the box caching using &lt;span class="caps"&gt;HTTP&lt;/span&gt; ETags and advanced behaviour caching&lt;/li&gt;
	&lt;li&gt;Added new &lt;code&gt;apply_behaviours&lt;/code&gt;&lt;/pre&gt; functionality for applying multiple behaviours at once.&lt;/li&gt;
	&lt;li&gt;Added a series of behaviour helpers to make applying Scriptaculous effects unobtrusively easy.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are also some bug fixes, some refactorings and significantly improved test coverage. For full details, see the announcement on the &lt;a href="http://www.ujs4rails.com"&gt;&lt;span class="caps"&gt;UJS&lt;/span&gt; website&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Finally, the other big change is that the Subversion repository has changed location, as has the place to report issues with the plugin: the plugin now has its own dedicated Trac.&lt;/p&gt;
&lt;p&gt;The plugin repository is now located at:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://source.ujs4rails.com"&gt;http://source.ujs4rails.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The latest version of the plugin now has its own tag, which should make it work with Rails&amp;#8217; built-in script/plugin script:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://source.ujs4rails.com/current/unobtrusive_javascript"&gt;http://source.ujs4rails.com/current/unobtrusive_javascript&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Finally, you can report issues on the &lt;span class="caps"&gt;UJS&lt;/span&gt; Rails Plugin Trac:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://trac.ujs4rails.com"&gt;http://trac.ujs4rails.com&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=YVg7YeGiIgg:mLztMyasYxs:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=YVg7YeGiIgg:mLztMyasYxs:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=YVg7YeGiIgg:mLztMyasYxs:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=YVg7YeGiIgg:mLztMyasYxs:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=YVg7YeGiIgg:mLztMyasYxs:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=YVg7YeGiIgg:mLztMyasYxs:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=YVg7YeGiIgg:mLztMyasYxs:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=YVg7YeGiIgg:mLztMyasYxs:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/YVg7YeGiIgg" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/ujs-rails-plugin-0-3-new-name-new-website.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/rails-plugin-repository-is-on-the-way.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/67c7LR0IT-Q/rails-plugin-repository-is-on-the-way.html" rel="alternate" />
    <published>2006-08-18T13:06:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>Rails plugin repository *is* on the way</title>
    <content type="html">&lt;p&gt;&lt;a href="http://www.ryandaigle.com"&gt;Ryan Daigle&lt;/a&gt; &lt;a href="http://www.ryandaigle.com/articles/2006/08/17/in-the-works-an-official-rails-plugin-repository"&gt;speculated on his blog&lt;/a&gt; that an official Rails plugin repository was in the works. He referred to an app checked into the Rails subversion repository recently.&lt;/p&gt;
&lt;p&gt;I can announce that a Rails plugin repository &lt;strong&gt;is&lt;/strong&gt; on the way, although not the one currently in the Rails repository. That particular app was a repository started by &lt;a href="http://technoweenie.net"&gt;Rick Olson&lt;/a&gt;. The repository that is on the way is actually being worked on by myself and &lt;a href="http://interblah.net"&gt;James Adam&lt;/a&gt; of &lt;a href="http://rails-engines.org/"&gt;Rails Engines&lt;/a&gt; fame.&lt;/p&gt;
&lt;h3&gt;Background and motivation&lt;/h3&gt;
&lt;p&gt;To give a bit of background to this; the idea of an official plugin repository was born out of a conversation between Rick Olson, James and &lt;a href="http://topfunky.com"&gt;Geoffrey Grosenbach&lt;/a&gt; at RailsConf. Work had already begun on improving the Rails plugin mechanism with &lt;a href="http://rapt.rubyforge.org"&gt;RaPT&lt;/a&gt;, a project run by Geoffrey, James and &lt;a href="http://bencurtis.com/"&gt;Ben Curtis&lt;/a&gt;. I recently joined the RaPT team and after some discussion with the rest of the team, James and I started development on the project.&lt;/p&gt;
&lt;p&gt;Some people may ask why an official repository is needed. Ben Curtis currently maintains a &lt;a href="http://agilewebdevelopment.com/"&gt;good repository&lt;/a&gt; of his own however it is unfair to rely on one person to keep the repository running. As more and more people use, develop and rely on plugins, the existing setup &amp;#8211; a wiki page &amp;#8211; is simply not scalable or maintainable. Plugins are the number one way of adding/changing Rails functionality without having to submit patches to the core &amp;#8211; something that &lt;a href="http://loudthinking.com"&gt;&lt;span class="caps"&gt;DHH&lt;/span&gt;&lt;/a&gt; actively encourages and something I fully support. I believe the core &lt;strong&gt;should&lt;/strong&gt; be as lean as possible with additional functionality being provided by plugins.&lt;/p&gt;
&lt;p&gt;Our original idea was to develop a plugin repository that sat somewhere between &lt;a href="http://rubyforge.org"&gt;Rubyforge&lt;/a&gt; and the &lt;a href="https://addons.mozilla.org/"&gt;Mozilla Firefox extensions website&lt;/a&gt; &amp;#8211; this included tagging, comments and ratings &amp;#8211; the usual Web 2.0 suspects. However, after some discussion with the Rails core team we decided to drop those features in favour of making adding plugins as easy as possible and developing a way of detecting plugin health by gathering test run statistics to both encourage testing of plugins and make it clear how likely it is that a plugin will work with a particular Ruby/Rails version. The other downside to having ratings/comments etc. was the need to make users sign up for an account, something that is best avoided.&lt;/p&gt;
&lt;p&gt;The other major feature that James and I felt was really important was the correct (and mostly automatic) handling of plugin releases and metadata. This includes reading plugin metadata from an about.yml file and handling plugin releases through either updates to the release number in the about.yml file or repository tags. One of the major failings of the current script/plugin tool is its inability to handle plugins that use the traditional trunk/tags/branches structure &amp;#8211; something I feel is essential for anything but trivial plugins.&lt;/p&gt;
&lt;p&gt;To sum up, the core functionality of the repository will be:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Make it easy to browse plugins and view plugin/release health/compatibility&lt;/li&gt;
	&lt;li&gt;Correctly and automatically handle plugin releases/versions and metadata&lt;/li&gt;
	&lt;li&gt;Make adding of plugins &amp;#8211; either single plugins or entire repositories of plugins &amp;#8211; as easy and foolproof as possible&lt;/li&gt;
	&lt;li&gt;Provide some Rake tasks to make it possible to announce new plugins/releases without having to go to the repository website&lt;/li&gt;
	&lt;li&gt;Improve the script/plugin tool and make it use the plugin repository as its main source, making it possible to install plugins by simply specifying the plugin name&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Current status of the project.&lt;/h3&gt;
&lt;p&gt;James and I have made good progress on the project so far. You can view where we are up to &lt;a href="http://leviathon.agileevolved.com/"&gt;here&lt;/a&gt; &amp;#8211; do bear in mind that this is still under development and cannot be considered reliable at this point. We are aiming to move the project over to the Rails subversion repository in the next few days.&lt;/p&gt;
&lt;p&gt;I cannot give an accurate &lt;span class="caps"&gt;ETA&lt;/span&gt; on when the project will be ready to go live, but we really want to have this up and running in time for the European RailsConf. In the meantime, have a play with what we&amp;#8217;ve done so far and get a feel for it. If you have any suggestions please do speak up as it is the community as a whole that this is aimed at.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: James has also put up a &lt;a href="http://interblah.net/2006/8/18/the-rails-plugins-repository"&gt;post on his blog&lt;/a&gt; about the project.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=67c7LR0IT-Q:9pbS8-qrwU4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=67c7LR0IT-Q:9pbS8-qrwU4:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=67c7LR0IT-Q:9pbS8-qrwU4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=67c7LR0IT-Q:9pbS8-qrwU4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=67c7LR0IT-Q:9pbS8-qrwU4:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=67c7LR0IT-Q:9pbS8-qrwU4:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=67c7LR0IT-Q:9pbS8-qrwU4:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=67c7LR0IT-Q:9pbS8-qrwU4:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/67c7LR0IT-Q" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/rails-plugin-repository-is-on-the-way.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/new-blogging-engine-and-template.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/UWrhQkZZfCk/new-blogging-engine-and-template.html" rel="alternate" />
    <published>2006-08-18T11:35:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>New blogging engine and template</title>
    <content type="html">&lt;p&gt;Like many other people, I&amp;#8217;ve moved my blog over to &lt;a href="http://mephistoblog.com"&gt;Mephisto&lt;/a&gt; by Olson and Palmer (doesn&amp;#8217;t that sound like a 70s prog band?). I&amp;#8217;d become very sick of Hemmingway though, even if I was using the less common white version, and with Mephisto coming with Hemmingway as its default theme its now even less unique, so it was time for a change. So I present to you&amp;#8230;Sydney.&lt;/p&gt;
&lt;p&gt;Why Sydney? Absolutely no reason whatsoever. It was the first thing that came into my head. The template itself wasn&amp;#8217;t actually designed for my blog; it was designed for the new, forthcoming Unobtrusive Javascript plugin website, with a different color scheme. A bit of reworking later I now have it as a full-on blogging template for Mephisto and it supports multiple color schemes. The &lt;span class="caps"&gt;UJS&lt;/span&gt; website will be using &amp;#8220;neopolitan&amp;#8221;. I call this one &amp;#8220;mintchoc&amp;#8221;. I&amp;#8217;m not 100% certain if I will stick with this color scheme but I&amp;#8217;m very happy with the overall layout.&lt;/p&gt;
&lt;p&gt;Unfortunately I will not be releasing this template for others to use&amp;hellip;to begin with anyway. Its nice to have your own unique theme so I&amp;#8217;d like to keep it that way for the time being. I have no doubt that I will release the template to the community in the future though.&lt;/p&gt;
&lt;h3&gt;Backwards compatibility&lt;/h3&gt;
&lt;p&gt;I&amp;#8217;ve added a custom route to make sure all Wordpress URLs load the correct article &amp;#8211; the only difference was the /index.php/ part of the Wordpress &lt;span class="caps"&gt;URL&lt;/span&gt; and a few of the article slugs needed tweaking. If you have any pages bookmarked, I&amp;#8217;d recommend updating your bookmarks to the new &lt;span class="caps"&gt;URL&lt;/span&gt;. Archive pages will probably not work any more. I&amp;#8217;m also yet to import the comments but I will do this over the coming days. Thanks to Chris McGrath for his &lt;a href="http://octopod.info/2006/6/12/converting-typo-to-mephisto"&gt;article on converting typo URLs to mephisto URLs&lt;/a&gt; which was a great help in getting this running properly.&lt;/p&gt;
&lt;h3&gt;Awesome syntax highlighting&lt;/h3&gt;
&lt;p&gt;It&amp;#8217;s also worth mentioning that code syntax highlighting is provided by Dan Webb&amp;#8217;s awesome Javascript CodeHighlighter library, and a small Rails plugin that adds some Mephisto filters to use it. Dan hasn&amp;#8217;t set up a permanent home for the library yet and some of the language definition files could do with a bit of fleshing out, but its very easy to extend and its very fast. Naturally, its completely unobtrusive &amp;#8211; your code will still be displayed sans-highlighting if Javascript is disabled and there&amp;#8217;s no more littering of your code with loads of &lt;code&gt;span&lt;/code&gt; tags. Best of all, its completely language agnostic &amp;#8211; use it with your Rails apps, your &lt;span class="caps"&gt;PHP&lt;/span&gt; apps, or with completely static &lt;span class="caps"&gt;HTML&lt;/span&gt; pages. &lt;a href="http://projects.danwebb.net/wiki/CodeHighlighter"&gt;Grab CodeHighlighter for your own website&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;And finally&amp;hellip;&lt;/h3&gt;
&lt;p&gt;On the subject of the &lt;span class="caps"&gt;UJS&lt;/span&gt; website, Dan and I hope to have this, along with a 0.3 release out on Monday. The new feature contains some neat new features, some bug fixes, improved test coverage and most importantly&amp;#8230;caching. Keep an eye out on Monday for more information and the new &lt;span class="caps"&gt;UJS&lt;/span&gt; Rails Plugin website.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=UWrhQkZZfCk:k74HYEmSHYs:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=UWrhQkZZfCk:k74HYEmSHYs:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=UWrhQkZZfCk:k74HYEmSHYs:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=UWrhQkZZfCk:k74HYEmSHYs:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=UWrhQkZZfCk:k74HYEmSHYs:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=UWrhQkZZfCk:k74HYEmSHYs:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=UWrhQkZZfCk:k74HYEmSHYs:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=UWrhQkZZfCk:k74HYEmSHYs:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/UWrhQkZZfCk" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/new-blogging-engine-and-template.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/unobtrusive-js-022-the-two-in-one-day-release.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/ESnVaNejNvk/unobtrusive-js-022-the-two-in-one-day-release.html" rel="alternate" />
    <published>2006-08-10T17:55:00Z</published>
    <updated>2009-02-15T10:04:52Z</updated>
    <title>Unobtrusive JS 0.2.2 - "the two in one day!" release</title>
    <content type="html">&lt;div class="notice"&gt;Update 15 Feb 2009: The &lt;span class="caps"&gt;UJS&lt;/span&gt; plugin for Rails is no longer actively maintained but you can find the &lt;a href="http://github.com/lukeredpath/ujs4rails"&gt;the source on github&lt;/a&gt;.&lt;/div&gt;
&lt;p&gt;I&amp;#8217;m sure many people are aware of the risks in running pre-1.0 software/plugins and that there are likely to be many bugs discovered until that 1.0 release finally hits (and beyond).&lt;/p&gt;
&lt;p&gt;Unfortunately, it takes really working with a Rails plugin to find problems and I have been doing so today. I encountered a strange error where the plugin was generating IDs for elements that shouldn&amp;#8217;t have any javascript events attached.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Update 21/08/2006&lt;/b&gt;: The latest version of this plugin is 0.3 &amp;#8211; please see [this post](http://www.lukeredpath.co.uk/2006/8/21/ujs-rails-plugin-0-3-new-name-new-website) and the [official &lt;span class="caps"&gt;UJS&lt;/span&gt; website](http://www.ujs4rails.com) for more information.&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;def tag_options(opts)
  unless opts[:inline]
    JAVASCRIPT_EVENTS.each do |event|
      if opts.include?("on#{event}") # this was the culprit!
        opts['id'] = generate_html_id unless opts['id']
        register_js_behaviour("##{opts['id']}:#{event}",
                  opts["on#{event}"]) unless opts["on#{event}"].nil?
        opts.delete("on#{event}")
      end
    end
  end
  rails_tag_options(opts)
end&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;What it turned out that was happening was that some Rails helpers were adding &lt;strong&gt;empty&lt;/strong&gt; onclick handlers to some tags so they were being picked up by the above code. All that was required was a simple fix:&lt;/p&gt;
&lt;pre class="ruby"&gt;&lt;code class="ruby"&gt;# get rid of this
# if opts.include?("on#{event}") 
# and use this:
unless opts["on#{event}"].blank?&lt;/code&gt;

&lt;/pre&gt;
&lt;p&gt;So [grab Unobtrusive Javascript for Rails 0.2.2](http://opensource.agileevolved.com/svn/root/rails_plugins/unobtrusive_javascript/tags/rel-0.2.2/) while its hot!&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=ESnVaNejNvk:ZMA0o9B-TK4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=ESnVaNejNvk:ZMA0o9B-TK4:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=ESnVaNejNvk:ZMA0o9B-TK4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=ESnVaNejNvk:ZMA0o9B-TK4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=ESnVaNejNvk:ZMA0o9B-TK4:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=ESnVaNejNvk:ZMA0o9B-TK4:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=ESnVaNejNvk:ZMA0o9B-TK4:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=ESnVaNejNvk:ZMA0o9B-TK4:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/ESnVaNejNvk" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/unobtrusive-js-022-the-two-in-one-day-release.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/unobtrusive-javascript-broken-in-edge-rails-small-fix-release.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/xK6524Tqdzc/unobtrusive-javascript-broken-in-edge-rails-small-fix-release.html" rel="alternate" />
    <published>2006-08-10T12:26:00Z</published>
    <updated>2009-02-15T10:04:52Z</updated>
    <title>Unobtrusive Javascript broken in Edge Rails, small fix release</title>
    <content type="html">&lt;div class="notice"&gt;Update 15 Feb 2009: The UJS plugin for Rails is no longer actively maintained but you can find the &lt;a href="http://github.com/lukeredpath/ujs4rails"&gt;the source on github&lt;/a&gt;.&lt;/div&gt;


&lt;p&gt;We're currently using Edge Rails and the Unobtrusive Javascript for Rails plugin in the forthcoming &lt;a href="http://svn.lazyatom.com/public/plugin_repository/trunk/"&gt;Rails Plugin Repository&lt;/a&gt; and after a Subversion update today, something seems to have broken the Unobtrusive Javascript plugin.&lt;/p&gt;

&lt;p&gt;I'm not sure what caused the breakage but the problem was a minor one; after some investigation it turned out that the unobtrusive javascript controller was not being automatically being required by Rails even though its parent folder had been added to the Rails load path. The fix was as simple as explicitly requiring the controller file and everything is now fine. &lt;del&gt;&lt;a href="http://opensource.agileevolved.com/svn/root/rails_plugins/unobtrusive_javascript/tags/rel-0.2.1/"&gt;Release 0.2.1&lt;/a&gt;&lt;/del&gt; addresses this issue and also adds an about.yml file containing plugin meta-data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: &lt;ins&gt;&lt;a href="/index.php/2006/08/10/unobtrusive-js-022-the-two-in-one-day-release/"&gt;Please grab Release 0.2.2 instead&lt;/a&gt;.&lt;/ins&gt;&lt;/p&gt;

&lt;p&gt;If you are running on Edge Rails you will need to upgrade to this release. The plugin (version 0.2) continues to work just fine if you are running the new &lt;a href="http://weblog.rubyonrails.org/2006/8/9/rails-1-1-5-mandatory-security-patch-and-other-tidbits"&gt;Rails 1.1.5 security fix&lt;/a&gt; so whatever caused this breakage doesn't appear to be related to the security fix.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=xK6524Tqdzc:akU2XTapA2I:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=xK6524Tqdzc:akU2XTapA2I:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=xK6524Tqdzc:akU2XTapA2I:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=xK6524Tqdzc:akU2XTapA2I:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=xK6524Tqdzc:akU2XTapA2I:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=xK6524Tqdzc:akU2XTapA2I:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=xK6524Tqdzc:akU2XTapA2I:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=xK6524Tqdzc:akU2XTapA2I:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/xK6524Tqdzc" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/unobtrusive-javascript-broken-in-edge-rails-small-fix-release.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/the-railsconf-europe-hooha.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/VZk2A1DTgro/the-railsconf-europe-hooha.html" rel="alternate" />
    <published>2006-08-04T10:37:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>The RailsConf Europe HooHa</title>
    <content type="html">&lt;p&gt;&lt;a href="http://www.loudthinking.com"&gt;David&lt;/a&gt; posted an &lt;a href="http://svn.lazyatom.com/public/plugin_repository/trunk"&gt;entry&lt;/a&gt; on the RubyOnRails weblog pushing people to go to RailsConf EU and a lot of people reacted negatively to it.&lt;/p&gt;

&lt;p&gt;The thing that most people reacted badly about was the price. Â£575 is a lot of money to spend for most people, especially freelancers and enthusiasts and the early bird price of Â£475 is still not cheap.&lt;/p&gt;

&lt;p&gt;The more cynical would think that the real reason David wants to encourage people to come along is because the organisers can make money from it. I doubt this is the case. However there has been a surprising lack of enthusiasm and hype surrounding the conference compared to the US conference which was hyped to the roof and I suspect the number one reason for this is the cost.&lt;/p&gt;

&lt;p&gt;That being said, there is really little excuse for much of the whining and moaning that has been going in the comments for the above post and elsewhere. Ultimately, its not about whether or not the conference is expensive (and I &lt;em&gt;do&lt;/em&gt; think it is overpriced and yes, I am going) but whether or not you can justify the cost.&lt;/p&gt;

&lt;p&gt;If you can justify the cost, please, get a ticket and come along - I do feel it will be a worthwhile experience. You'll get to meet some interesting and clever people, talk Ruby and Rails for two days without people looking at you funny and its a great networking opportunity.&lt;/p&gt;

&lt;p&gt;If you can't justify the cost - fair enough. But don't go on about it - get over it; move on. The world is not going to end because you could not attend the conference. Your RubyOnRails skills are not going to suffer. I'd consider myself a good Ruby and Rails programmer and I didn't get to the level I'm at today with a full time, well-paid Rails job by going to conferences. Even if I could have &lt;em&gt;afforded&lt;/em&gt; the flight and tickets to the US RailsConf (and I would have loved to go) there was no way I'd be able to justify that expense.&lt;/p&gt;

&lt;p&gt;Many people mentioned that the money they could spend on the conference would be better spent on books. Thats a fair point. If you have the money available do go and buy some books, you'll definitely benefit. I strongly recommend &lt;a href="http://www.amazon.co.uk/gp/product/0977616606/sr=8-1/qid=1154683085/ref=pd_ka_1/202-6841703-3484634?ie=UTF8&amp;amp;s=gateway"&gt;Rails Recipes&lt;/a&gt; by Chad Fowler and &lt;a href="http://www.amazon.co.uk/gp/product/0321125215/sr=8-1/qid=1154683108/ref=pd_ka_1/202-6841703-3484634?ie=UTF8&amp;amp;s=gateway"&gt;Domain Driven Design&lt;/a&gt; by Eric Evans.&lt;/p&gt;

&lt;p&gt;As I said I do believe the conference is far too expensive; but some of the bile being aimed at the organisers is ridiculous. Yes it would be nice to go but going to a conference is a privilege, not a necessity. Nobody is pointing a gun at your head and making you go. If you are in the UK, you could always try visiting a local Ruby Users Group, or starting your own if there isn't one.&lt;/p&gt;

&lt;p&gt;Regarding the price, if the main reason that it is so expensive is because it is being held in London - one of the most expensive cities in the world (I know, I live there!) then my advice to the organisers would be: hold it somewhere else. There are much cheaper venues with great air, road and rail transport links - Birmingham springs to mind. With the wealth of cheap flights out there, I'd even be happy to fly to somewhere on the continent for the conference - it would still probably work out cheaper.&lt;/p&gt;

&lt;p&gt;Finally, a lot of people were bitching about such and such feature not being in Rails (il8n mainly) and how Rails was making a fatal mistake by not having it in the core. If people haven't really picked up on this yet, let me re-iterate - Rails is &lt;em&gt;opinionated&lt;/em&gt; software and I hope it stays that way. Yes there are times when I've wished for a feature but with Rails' plugin system there is no longer any excuse for bugging the core team for a particular feature - if you need it that badly, implement it as a plugin and share it with the world. There are already efforts going into &lt;a href="http://www.globalize-rails.org/"&gt;internationalisation&lt;/a&gt; and &lt;a href="http://compositekeys.rubyforge.org/"&gt;legacy database support&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The core should only ever have the key features that are essential to the majority of people - adding minority features (even if they are important minority features) will only lead to a bloated framework which is a contradiction to everything Rails is supposed to stand for. ActionWebService is already being stripped from the core into a plugin and there are other things I'd like to see extracted from the core into plugins as well - the Prototype/Scriptaculous helpers and most importantly the most useless Rails feature of all, scaffolding - a feature that anybody with an ounce of Rails experience has a need for other than for the occasional spike/prototype.&lt;/p&gt;

&lt;p&gt;I'm well aware that the current Rails plugin system is far from perfect - installing plugins could be made easier and its a pain to install plugins that make use of the standard Subversion trunk/tags/branches structure. But there are efforts &lt;a href="http://rubyforge.org/projects/rapt/"&gt;going&lt;/a&gt; &lt;a href="http://svn.lazyatom.com/public/plugin_repository/trunk/"&gt;on&lt;/a&gt; to improve this. If theres one thing you can do today to improve Rails and make yourself a more experienced Rails programmer - &lt;a href="http://nubyonrails.com/articles/2006/05/04/the-complete-guide-to-rails-plugins-part-i"&gt;write a plugin&lt;/a&gt;!&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=VZk2A1DTgro:6ynXy8x5LWk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=VZk2A1DTgro:6ynXy8x5LWk:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=VZk2A1DTgro:6ynXy8x5LWk:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=VZk2A1DTgro:6ynXy8x5LWk:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=VZk2A1DTgro:6ynXy8x5LWk:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=VZk2A1DTgro:6ynXy8x5LWk:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=VZk2A1DTgro:6ynXy8x5LWk:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=VZk2A1DTgro:6ynXy8x5LWk:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/VZk2A1DTgro" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/the-railsconf-europe-hooha.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/release-unobtrusive-javascript-for-rails-02.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/fdJFl9hvnPE/release-unobtrusive-javascript-for-rails-02.html" rel="alternate" />
    <published>2006-08-01T03:17:00Z</published>
    <updated>2009-02-15T10:04:52Z</updated>
    <title>Release: Unobtrusive Javascript For Rails 0.2</title>
    <content type="html">&lt;div class="notice"&gt;Update 15 Feb 2009: The UJS plugin for Rails is no longer actively maintained but you can find the &lt;a href="http://github.com/lukeredpath/ujs4rails"&gt;the source on github&lt;/a&gt;.&lt;/div&gt;


&lt;p&gt;&lt;a href="http://www.vivabit.com/bollocks"&gt;Dan Webb&lt;/a&gt; and I are happy to announce the latest release of the Unobtrusive Javascript plugin for Rails. This release packs in lots of cool new features, a few changes to old ones and some performance enhancements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update 21/08/2006&lt;/strong&gt;: The latest version of this plugin is 0.3 - please see &lt;a href="http://www.lukeredpath.co.uk/2006/8/21/ujs-rails-plugin-0-3-new-name-new-website"&gt;this post&lt;/a&gt; and the &lt;a href="http://www.ujs4rails.com"&gt;official UJS website&lt;/a&gt; for more information.&lt;/p&gt;

&lt;p&gt;As I mentioned in &lt;a href="http://www.lukeredpath.co.uk/index.php/2006/06/06/introducing-unobtrusive-javascript-for-rails/"&gt;my previous article&lt;/a&gt;, Dan had been working on his own plugin and we've now pooled our efforts and are working on the single plugin together, with much of Dan's JavascriptProxy functionality from his own plugin now rolled into Unobtrusive Javascript for Rails 0.2.&lt;/p&gt;

&lt;p&gt;Here's a full list of changes and additions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UPDATED: register_js_behaviour remains as an alias for backwards compatibility but is officially deprecated; it will probably be removed in the next release. There are also aliases for the American spelling of behaviour for our friends on the other side of the pond.&lt;/li&gt;
&lt;li&gt;NEW: You can now attach behaviours directly to elements using @tag@&lt;/pre&gt; by passing in javascript event handlers in the HTML options hash (:onclick, :onmousedown etc.); they'll be extracted to the external behaviours file automatically.&lt;/li&gt;
&lt;li&gt;UPDATED: The core Rails AJAX/Javascript helpers (&lt;code&gt;link_to_remote, button_to_function, link_to_function, form_remote_tag&lt;/code&gt; etc.) now work out of the box.&lt;/li&gt;
&lt;li&gt;NEW: There is no need to explicitly specify an HTML ID for the elements you want to attach behaviour to - if you don't, one will be generated automatically for you.&lt;/li&gt;
&lt;li&gt;NEW: Options to render behaviour rules directly in your page inside script blocks instead of in the external behaviour file.&lt;/li&gt;
&lt;li&gt;FIXED: Behaviours inside AJAX-loaded partials will now work.&lt;/li&gt;
&lt;li&gt;UPDATED: event:Selectors and domReady javascript libraries are replaced with the lowPro.js library by Dan Webb&lt;/li&gt;
&lt;li&gt;UPDATED: Javascript behaviours now have access to a single variable - @this@&lt;/li&gt;
&lt;li&gt;UPDATED: Behaviours can now cancel the default action by returning false as well as using Event.stop(event). This also works properly in Safari.&lt;/li&gt;
&lt;li&gt;FIXED: The required javascript files will be copied to your public folder automatically on installation. There is also a rake task for copying files across when upgrading the plugin.&lt;/li&gt;
&lt;li&gt;NEW: Documentation!&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;There is currently only one known issue:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Behaviours are not being reapplied after an AJAX request in Opera 9&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Here's a look at some of the new features, starting with writing behaviours using pure Ruby:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;% apply_behaviour "#mylink:click" do |page, element|
  page.alert "You clicked me! Now watch me fade..."
  element.visual_effect :fade
end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Attaching behaviours directly to elements with &lt;code&gt;content_tag&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;%= content_tag "div", "My funky box", :onclick =&amp;gt; "alert('Hello World!')" %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We want you to really put this release through its paces - does it conflict with other plugins? Does it cause unexpected problems with Rails itself? Are there any other features you'd like to see? The place to report these feature requests and bugs is of course &lt;a href="http://opensource.agileevolved.com/trac/newticket"&gt;the Agile Evolved Open Source Trac&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;Further Information&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://opensource.agileevolved.com/svn/root/rails_plugins/unobtrusive_javascript/tags/rel-0.2"&gt;Unobtrusive Javascript for Rails Subversion URL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://opensource.agileevolved.com/unobtrusivejs/"&gt;Plugin Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;&lt;strong&gt;Installation note&lt;/strong&gt;: some people have &lt;a href="http://opensource.agileevolved.com/trac/ticket/9"&gt;reported&lt;/a&gt; that the plugin fails because the Rails plugin installation script installs the plugin to rel-0.2 instead of unobtrusive_javascript. This is a failing of Rails' plugin installation system in that it doesn't correctly handle the typical tags/trunk/branches Suvbersion repository layout. If you install the plugin using script/plugin, make sure you rename the rel-0.2 folder to unobtrusive_javascript. If you are using svn:externals, you can edit your externals file manually:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ svn propedit svn:externals vendor/plugins
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then add the following line to it:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;unobtrusive_javascript http://opensource.agileevolved.com/svn/root/rails_plugins/unobtrusive_javascript/tags/rel-0.2
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Future Plans&lt;/h3&gt;

&lt;p&gt;So what do we have planned for 0.3? More patches to Rails' more complicated built-in helpers, such as &lt;code&gt;apply_behaviour&lt;/code&gt; how about some of this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;% apply_behaviour "div.draggable", make_draggable %&amp;gt;
&amp;lt;% apply_behaviour "li.sortable", make_sortable %&amp;gt;
&amp;lt;% apply_behaviour "#searchbox", make_autocomplete %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Comments open!&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=fdJFl9hvnPE:1h9oKn2EKMw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=fdJFl9hvnPE:1h9oKn2EKMw:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=fdJFl9hvnPE:1h9oKn2EKMw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=fdJFl9hvnPE:1h9oKn2EKMw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=fdJFl9hvnPE:1h9oKn2EKMw:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=fdJFl9hvnPE:1h9oKn2EKMw:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=fdJFl9hvnPE:1h9oKn2EKMw:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=fdJFl9hvnPE:1h9oKn2EKMw:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/fdJFl9hvnPE" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/release-unobtrusive-javascript-for-rails-02.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/rails-plugin-packs-coming-to-rapt.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/-06HRmueKFU/rails-plugin-packs-coming-to-rapt.html" rel="alternate" />
    <published>2006-07-28T17:00:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>Rails Plugin Packs&amp;trade; coming to RaPT</title>
    <content type="html">&lt;p&gt;Good news - &lt;a href="http://www.topfunky.com"&gt;Geoffrey&lt;/a&gt; has agreed to roll Rails Plugin Packs into RaPT. I've already integrated it into the RaPT source code and a patch is winging its way to Geoffrey's inbox as I write this. &lt;/p&gt;

&lt;p&gt;For the adventurous who want to give it a try in the meantime (with all the usual caveats), you need to first check out the RaPT source:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ svn co svn://rubyforge.org/var/svn/rapt/trunk rapt
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;del&gt;Next, &lt;a href="http://www.lukeredpath.co.uk/uploads/ruby/add_plugin_packs_to_rapt.diff"&gt;download the pluginpack integration patch&lt;/a&gt;. Change to the rapt directory you just checked the source out to and apply the patch.&lt;/dl&gt;&lt;/p&gt;

&lt;p&gt;&lt;ins&gt;&lt;strong&gt;Update&lt;/strong&gt;: I've been added to the RaPT committers list; this patch has now been applied to the trunk. Once you've checked it out, you simply need to build and install the gem.&lt;/ins&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ rake package
$ gem install pkg/rapt-0.1.0.gem
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You'll find RaPT will now have three new commands: pack:install, pack:uninstall and pack:about. All three commands take one argument, the system path or URL to your pack file. Run rapt -h for more information.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=-06HRmueKFU:lVcjIFpQ0og:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=-06HRmueKFU:lVcjIFpQ0og:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=-06HRmueKFU:lVcjIFpQ0og:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=-06HRmueKFU:lVcjIFpQ0og:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=-06HRmueKFU:lVcjIFpQ0og:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=-06HRmueKFU:lVcjIFpQ0og:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=-06HRmueKFU:lVcjIFpQ0og:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=-06HRmueKFU:lVcjIFpQ0og:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/-06HRmueKFU" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/rails-plugin-packs-coming-to-rapt.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/install-your-favorite-rails-plugins-easily-with-rails-plugin-packs.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/wGiEll0HxKI/install-your-favorite-rails-plugins-easily-with-rails-plugin-packs.html" rel="alternate" />
    <published>2006-07-27T12:21:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>Install your favorite rails plugins easily with Rails Plugin Packs&amp;trade;</title>
    <content type="html">&lt;p&gt;After a brief brainwave on the way home from work, I came up with a simple solution to the problem, which I now present to you simply as: Rails Plugin Packsâ„¢.&lt;/p&gt;

&lt;p&gt;The concept is incredibly simple; you write your own plugin pack - nothing more than a simple specification written in YAML - and publish it somewhere, either storing it somewhere on your harddrive, on your network or hosted on your website. You then use a series of simple Rake tasks to install/uninstall these packs by simply specifying the URL/path to the pack file.&lt;/p&gt;

&lt;p&gt;I've initially implemented this as a Rails plugin, so install it to get going:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ ./script/plugin install http://opensource.agileevolved.com/svn/root/rails_plugin_pack
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This will give you three new Rake tasks - pluginpack:install, pluginpack:uninstall and pluginpack:about. What they do should be pretty self-explanatory. They all take a pack url/path as an argument, in the form PACK=url_or_path, for example:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ rake pluginpack:install PACK=http://www.lukeredpath.co.uk/mygreatpack.pluginpack
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Currently, Subversion is required to use the plugin as it installs each plugin by doing an svn co of each plugin. I plan to expand on the ways of installing the plugins soon, including support for svn export and svn:externals.&lt;/p&gt;

&lt;p&gt;Writing your own pluginpacks is simple - the file extension can be whatever you like although I prefer to use the standard .pluginpack suffix. The files themselves are very simple and are written in &lt;a href="http://www.yaml.org/"&gt;YAML&lt;/a&gt;. Here's an example plugin pack file (which comes bundled with the rails_plugin_pack plugin itself):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;about:
  name: Example Pack
  description: An example plugin pack
  author: Luke Redpath
  email: contact@lukeredpath.co.uk
  website: http://www.lukeredpath.co.uk
plugins:
  unobtrusive_javascript: http://opensource.agileevolved.com/..[truncated]
  navigation_mappings: http://opensource.agileevolved.com/..[truncated]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;All of the meta-data is optional, although if you omit a name it will result in your plugin being called "Untitled Plugin" so I recommend giving every one of your packs a name, at least.&lt;/p&gt;

&lt;p&gt;So what possible uses could this have? I've already discussed facilitating the installation of the same group of plugins on each new project - you could have your own personal or company-wide "standard" plugin pack that you use on each new project. You could write a blog post about your favorite Rails plugins and offer a plugin pack containing each plugin discussed in your article. You could use it to group related plugins. You could even use it as a basic dependency mechanism, by offering packs of plugins that depend on each other (although this is no substitute for a proper dependency mechanism).&lt;/p&gt;

&lt;p&gt;The only downside to this at the moment of course, is that Rails Plugin Packs is implemented as a Rails plugin itself, meaning you still need to install at least one plugin manually before you can do anything else on a new project. This isn't ideal and I'm waiting to hear back from &lt;a href="http://topfunky.com"&gt;Geoffrey Grosenbach&lt;/a&gt; about the possibility of submitting a patch to RaPT and rolling plugin-pack functionality into the RaPT core.&lt;/p&gt;

&lt;p&gt;Comments are open so as usual, all feedback welcome and appreciated; please file any bugs/issues in the &lt;a href="http://opensource.agileevolved.com"&gt;Agile Evolved Trac&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: I've made a small change to the plugin pack format so if you've already checked out the plugin you'll need to run an update. I've updated the example above and you can view the actual changes with the &lt;a href="http://opensource.agileevolved.com/trac/changeset/158"&gt;trac changeset viewer&lt;/a&gt;.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=wGiEll0HxKI:kNxY-op5xgA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=wGiEll0HxKI:kNxY-op5xgA:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=wGiEll0HxKI:kNxY-op5xgA:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=wGiEll0HxKI:kNxY-op5xgA:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=wGiEll0HxKI:kNxY-op5xgA:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=wGiEll0HxKI:kNxY-op5xgA:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=wGiEll0HxKI:kNxY-op5xgA:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=wGiEll0HxKI:kNxY-op5xgA:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/wGiEll0HxKI" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/install-your-favorite-rails-plugins-easily-with-rails-plugin-packs.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/using-ruby-hashes-as-keyword-arguments-with-easy-defaults.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/nRCu6fxrHEs/using-ruby-hashes-as-keyword-arguments-with-easy-defaults.html" rel="alternate" />
    <published>2006-07-27T10:36:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>Using Ruby hashes as keyword arguments, with easy defaults</title>
    <content type="html">&lt;p&gt;Similar to many Rails helpers/methods, a lot of the methods I write often use an optional hash of options, or sometimes just a hash only, to simulate keyword arguments (often using symbols).&lt;/p&gt;

&lt;p&gt;The only downside to doing this is you lose out on easily setting default values using Ruby's default method argument values. You might use code something similar to the following to make up for this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def some_method(opts={})
  my_foo =  opts[:foo] || 'mydefaultfoo'
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;However, as you have more and more keyword options, setting defaults in this way gets rather tedious. Fortunately, Ruby's Hash#merge comes to our rescue (almost) - it allows you to merge the contents of one hash with another. The only problem - any duplicate keys in the hash you are merging will overwrite your original hash values - when it comes to setting default values, we want this to work the other way around; we only want values in the defaults hash to be merged if they do not exist in the original hash. Again, Ruby comes to our rescue - Hash#merge takes a block as an argument and will pass any duplicate values that crop up into the block - we can use this block to decide which value to keep.&lt;/p&gt;

&lt;p&gt;Using the simple monkey patch to the Hash class below, you will no longer have to set each default individually:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class Hash
  def with_defaults(defaults)
    self.merge(defaults) { |key, old, new| old.nil? ? new : old } 
  end

  def with_defaults!(defaults)
    self.merge!(defaults) { |key, old, new| old.nil? ? new : old }
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Of course, sticking with Ruby naming conventions, with_defaults() will return a new hash whilst with_defaults!() will change the original hash directly. Now all you have to do is something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def my_funky_method(opts={})
  opts.with_defaults! {
    :arg_one =&amp;gt; 'foo',
    :arg_two =&amp;gt; 'bar',
    :arg_three =&amp;gt; 'baz'
  }
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: &lt;a href="http://www.vivabit.com/bollocks/"&gt;Dan Web&lt;/a&gt; just informed me of another way of doing this - instead of merging your defaults into your options, simply merge your options into your defaults et voila!&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def my_method(opts={})
  {:arg_one =&amp;gt; 'foo', :arg_two =&amp;gt; 'two'}.merge!(opts)
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If there is one thing that is annoying about Ruby, it's that just when you think you've come up with a cool little code snippet, somebody inevitably comes up with an even easier way of doing the same thing. &lt;/p&gt;

&lt;p&gt;That said, I do still think there is something nice about the with_defaults() method - it's slightly more intention-revealing. Weigh that up with the need to monkey-patch. Choose what works best for you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update 2&lt;/strong&gt;: Josh Susser now informs me (see comments) that this functionality is built-right into Rails courtesy of &lt;a href="http://api.rubyonrails.org/classes/ActiveSupport/CoreExtensions/Hash/ReverseMerge.html"&gt;ReverseMerge&lt;/a&gt;. I still think with_defaults sounds better but I guess that pretty much makes this post redundant!&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=nRCu6fxrHEs:3zxmxrh3jok:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=nRCu6fxrHEs:3zxmxrh3jok:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=nRCu6fxrHEs:3zxmxrh3jok:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=nRCu6fxrHEs:3zxmxrh3jok:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=nRCu6fxrHEs:3zxmxrh3jok:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=nRCu6fxrHEs:3zxmxrh3jok:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=nRCu6fxrHEs:3zxmxrh3jok:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=nRCu6fxrHEs:3zxmxrh3jok:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/nRCu6fxrHEs" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/using-ruby-hashes-as-keyword-arguments-with-easy-defaults.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/we-re-hiring-rails-developers-needed.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/ywYHUBusHYw/we-re-hiring-rails-developers-needed.html" rel="alternate" />
    <published>2006-07-24T16:29:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>We're hiring: Rails developers needed</title>
    <content type="html">&lt;p&gt;Things are getting busy here at &lt;a href="http://www.agileevolved.com"&gt;Agile Evolved&lt;/a&gt; and we are looking for UK-based RubyOnRails developers to join our team.&lt;/p&gt;

&lt;p&gt;We'e looking at two junior-level development positions. You donâ€™t necessarily need to have RubyOnRails experience but if you do not you will need to be a fast learner and have excellent skills in another language.&lt;/p&gt;

&lt;p&gt;You must fulfill the following requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Should be able to learn very very quickly.&lt;/li&gt;
&lt;li&gt;Should have excellent OO knowledge (be prepared to be tested on this).&lt;/li&gt;
&lt;li&gt;Experience with a scripting language such as either Ruby (preferred), Python, PHP 5 or similar.&lt;/li&gt;
&lt;li&gt;You must already have the the relevant UK work permits or be an EU resident.&lt;/li&gt;
&lt;li&gt;Good knowledge of HTML/XHTML and CSS.&lt;/li&gt;
&lt;li&gt;Basic knowledge of Javascript (Youâ€™ll be an expert by the time we'e through with you).&lt;/li&gt;
&lt;li&gt;You must like or at least smile when using a Mac.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;In addition, we'e also looking for a full-time Web Designer. Skills for this position are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Excellent HTML/XHTML and CSS skills.&lt;/li&gt;
&lt;li&gt;Good design skills.&lt;/li&gt;
&lt;li&gt;Knowledge of Photoshop and Illustrator.&lt;/li&gt;
&lt;li&gt;Good Javascript skills.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Of course if you'e one of those few people that can code and do design as well weâ€™d love to hear from you too.&lt;/p&gt;

&lt;p&gt;Salaries for these positions are in the Â£18k to Â£25k range. Please send your CV/Resume as a PDF (preferred), MS Word or text file to enquiries@agileevolved.com. Please also include a cover letter. No agencies please.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=ywYHUBusHYw:CZ52wea2cB0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=ywYHUBusHYw:CZ52wea2cB0:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=ywYHUBusHYw:CZ52wea2cB0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=ywYHUBusHYw:CZ52wea2cB0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=ywYHUBusHYw:CZ52wea2cB0:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=ywYHUBusHYw:CZ52wea2cB0:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=ywYHUBusHYw:CZ52wea2cB0:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=ywYHUBusHYw:CZ52wea2cB0:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/ywYHUBusHYw" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/we-re-hiring-rails-developers-needed.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/testing-your-rails-views-with-hpricot.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/8eMndkYIprY/testing-your-rails-views-with-hpricot.html" rel="alternate" />
    <published>2006-07-07T13:22:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>Testing your Rails views with Hpricot</title>
    <content type="html">&lt;p&gt;&lt;strong&gt;Update: This extension is now available as a Rails plugin. See below for more details.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.whytheluckystiff.net"&gt;_why&lt;/a&gt; released his great little &lt;a href="http://redhanded.hobix.com/inspect/okayGiveHpricot02AGo.html"&gt;Hpricot HTML parser&lt;/a&gt; the other day and the first thing that struck me would be that it would be a great tool for testing the output of Rails views. Rails comes with the built-in assert_tag method but I've always found it clunky and a pain in the arse to use.&lt;/p&gt;

&lt;p&gt;By combining the Hpricot library and a few useful helper functions, you can now test the output of your Rails views in Test::Unit::TestCase easily using Hpricot's support for CSS selector searches.&lt;/p&gt;

&lt;p&gt;First of all, install Hpricot if you don't have it already:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ gem install hpricot --source code.whytheluckystiff.net
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Next, &lt;a href="http://www.lukeredpath.co.uk/assets/2006/8/26/hpricot_test_extension.rb"&gt;download the Hpricot Test Extensions file&lt;/a&gt; and copy it to the lib folder of your Rails app.&lt;/p&gt;

&lt;p&gt;Finally, require the file at the top of your test_helper.rb file:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# test_helper.rb
require 'hpricot_test_extension'
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Alternatively, you can also install as a Rails plugin. The plugin is located in the &lt;a href="http://opensource.agileevolved.com/svn/root/rails_plugins/hpricot_test_helper/trunk"&gt;Agile Evolved Open Source repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And thats it. You'll now have access to four new methods - tag, tags, element and elements. All four methods take a CSS selector as an argument. The pluralised methods return an array of search results for the CSS query, the singular methods just return the first (handy if you know you'll only have one of a particular element, say, "title"). The tag methods return the inner contents of the returned elements, whereas the element methods return the raw Hpricot::Elem objects for you to work with. Here's a few examples:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;assert_equal "My Funky Website", tag('title')
assert_equal 20, tags('div.boxout').size
assert_equal 'visible', element('div#site_container').attributes['class']
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Finally, there is a small extension to the Hpricot::Elem object itself which lets you assert the presence of text within an element using an alternative syntax:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;assert element('body').should_contain('My Funky Website')
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I plan to integrate these helpers into &lt;a href="http://rspec.rubyforge.org"&gt;rSpec&lt;/a&gt; as well using a more rSpec-style syntax. Any comments and suggestions are welcome.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=8eMndkYIprY:F0xNozmW0gU:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=8eMndkYIprY:F0xNozmW0gU:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=8eMndkYIprY:F0xNozmW0gU:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=8eMndkYIprY:F0xNozmW0gU:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=8eMndkYIprY:F0xNozmW0gU:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=8eMndkYIprY:F0xNozmW0gU:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=8eMndkYIprY:F0xNozmW0gU:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=8eMndkYIprY:F0xNozmW0gU:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/8eMndkYIprY" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/testing-your-rails-views-with-hpricot.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/annoying-ie-bug-with-the-script-aculo-us-in-place-editor.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/Gu6IBiPsrVQ/annoying-ie-bug-with-the-script-aculo-us-in-place-editor.html" rel="alternate" />
    <published>2006-07-03T13:34:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>Annoying IE bug with the script.aculo.us in-place editor</title>
    <content type="html">&lt;p&gt;I came across a rather annoying bug with the &lt;a href="http://script.aculou.us"&gt;script.aculo.us&lt;/a&gt; effects library the other day. If you use pass in a loadTextUrl parameter to make the inline editor grab the value from the server, Internet Explorer would throw up the error &lt;em&gt;'Can't move focus to the control because it's invisible not enabled or of a type that does not accept focus.'&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;After digging around in the inplace-editor code, it seems that it was disabling the inplace form field while it sent off an AJAX request to the server to grab the value, then re-enabling it once it the data was returned. The problem seemed to be down to the difference in the way IE and other browsers place focus on the field once it was inserted. Safari and Firefox were both placing focus on the field before it was disabled, whilst IE was trying to focus it after it had been disabled. This is what was causing IE to barf.&lt;/p&gt;

&lt;p&gt;The solution? Instead of simply calling the function that retrieves the remote text from the server (which does both the disabling/reenabling and AJAX request) directly, attach it to the field's onfocus event handler. This ensures that it will always get executed &lt;em&gt;after&lt;/em&gt; the field has focus.&lt;/p&gt;

&lt;p&gt;I've submitted a &lt;a href="http://dev.rubyonrails.org/ticket/5557"&gt;patch&lt;/a&gt; for this which fixes this problem which will hopefully be accepted, so if you've ever had problems with this in IE, this should help you out.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=Gu6IBiPsrVQ:LqgNVOWFED0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=Gu6IBiPsrVQ:LqgNVOWFED0:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=Gu6IBiPsrVQ:LqgNVOWFED0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=Gu6IBiPsrVQ:LqgNVOWFED0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=Gu6IBiPsrVQ:LqgNVOWFED0:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=Gu6IBiPsrVQ:LqgNVOWFED0:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=Gu6IBiPsrVQ:LqgNVOWFED0:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=Gu6IBiPsrVQ:LqgNVOWFED0:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/Gu6IBiPsrVQ" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/annoying-ie-bug-with-the-script-aculo-us-in-place-editor.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/rubyslim-ruby-api-for-slimserver.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/27dtggivfO4/rubyslim-ruby-api-for-slimserver.html" rel="alternate" />
    <published>2006-07-03T11:15:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>RubySlim - Ruby API for SlimServer</title>
    <content type="html">&lt;p&gt;Just a small announcement for any other Squeezebox/Soundbridge owners out there who use the &lt;a href="http://www.slimdevices.com"&gt;SlimServer&lt;/a&gt; music server. I've started work on a Ruby API for it and you can view the latest source on the &lt;a href="http://opensource.agileevolved.com/trac/browser/rubyslim/trunk"&gt;Agile Evolved Open Source Trac&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can also check out the latest source code from &lt;a href="http://opensource.agileevolved.com/svn/root/rubyslim/trunk"&gt;Subversion&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here's a small code example:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;slimserver = RubySlim::SlimServer.open('localhost')
slimserver.connect('myusername', 'mypassword')
puts slimserver.version

squeezebox = slimserver.players.first
squeezebox.turn_on
squeezebox.current_playlist.play
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And in the future...a full RubyOnRails, AJAX-enabled front-end to the SlimServer for browsing your music collection, creating playlists, controlling your SqueezeBox and more. I've not checked anything in on this but I've already been experimenting with creating ActiveRecord models that interact with the SlimServer database. You could do something like this (this is actual code that I have got working):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;my_favorite_album = Album.find_by_title('The Number Of The Beast')
squeezebox.current_playlist.load :album =&amp;gt; my_favorite_album.id
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Finally, if you have access to the SlimServer CLI API (click on technical information on your SlimServer web interface menu) you can play with any parts of the CLI API that I'm yet to add to the Ruby API by using the raw connection and API object:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# make sure you are using the CLI port, not the web interface port
connection = RubySlim::SlimServer.raw_connection('localhost', 9090)
slimapi = RubySlim::SlimAPI.new(connection)

# invoke any api commands
slimapi.invoke("version")
slimapi.invoke("myplayerid player status")
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;For those interested in the concept of &lt;a href="http://behaviour-driven.org/"&gt;behaviour-driven development&lt;/a&gt;, the whole thing is specced using &lt;a href="http://rspec.rubyforge.org"&gt;rSpec&lt;/a&gt;. Admittedly its not the greatest exercise in BDD because I'm simply wrapping an existing API in a Ruby API so there wasn't more than 1 context for most classes, but it was still great to use and the built-in mock objects API -- which seems very similar to &lt;a href="http://onestepback.org/software/flexmock"&gt;FlexMock&lt;/a&gt; -- was very easy to get to grips with - great for mocking out those telnet connections to the SlimServer CLI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; RubySlim is now hosted as a &lt;a href="http://rubyforge.org/projects/rubyslim/"&gt;RubyForge project&lt;/a&gt;. The 0.1 release is available as a gem.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ gem install rubyslim
&lt;/code&gt;&lt;/pre&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=27dtggivfO4:Of5rcbhbLO4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=27dtggivfO4:Of5rcbhbLO4:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=27dtggivfO4:Of5rcbhbLO4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=27dtggivfO4:Of5rcbhbLO4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=27dtggivfO4:Of5rcbhbLO4:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=27dtggivfO4:Of5rcbhbLO4:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=27dtggivfO4:Of5rcbhbLO4:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=27dtggivfO4:Of5rcbhbLO4:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/27dtggivfO4" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/rubyslim-ruby-api-for-slimserver.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/updating-a-single-record-attribute-with-activerecord.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/nwlm_C67dI8/updating-a-single-record-attribute-with-activerecord.html" rel="alternate" />
    <published>2006-06-28T11:15:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>Updating a single record attribute with ActiveRecord</title>
    <content type="html">&lt;p&gt;On a Rails project I'm working on at the moment, the Rails app had to make calls to a third-party application using web services - we would then update certain attributes on a particular model instance with data returned by the web service. The only problem was that the third-party app had to update (different) attributes on the same record. Whilst the web service would always respond in a timely fashion, there was no way of predicting when the third-party app would run its update (it could be delayed if the server the server was under heavy load).&lt;/p&gt;

&lt;p&gt;This resulted in a race condition that could potentially result in a loss of data. Because Rails already had a loaded instance of the object in memory before the web service call was made, if the third-party app saved its changes before Rails saved the record with its own changes, the values already loaded in the Rails object would overwrite the new values written by the third-party ap&lt;/p&gt;

&lt;p&gt;The solution seemed obvious at first - instead of doing:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;obj.some_attribute = 'foo'
obj.save
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We could simply do:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;obj.update_attribute('some_attribute', 'foo')
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt; But we were mistaken. Whilst the method's name would seem to imply that ONLY a single attribute would be updated, this is in fact not the case. All update_attribute() does is exactly the same thing as the first code snippet. From the Rails source:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def update_attribute(name, value)
  send(name.to_s + '=', value)
  save
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt; As it turns out, there isn't a built-in way of updating just a single attribute that we could see. So we wrote our own, simple solution:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def update_single_attribute(attribute, value)
  connection.update(
    "UPDATE #{self.class.table_name} " +
    "SET #{attribute.to_s} = #{quote(value)} " +
    "WHERE #{self.class.primary_key} = #{quote(id)}",
    "#{self.class.name} Attribute Update"
  )
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Feel free to use this in your own apps if needed - the easiest way to use is it is as a &lt;a href="http://en.wikipedia.org/wiki/Monkey-Patch"&gt;monkey patch&lt;/a&gt; to ActiveRecord - simply paste the following code into a file called active_record_patches.rb in your app's lib folder and require it at the bottom of your environment.rb file. Alternatively, you could patch the Rails source directly or convert this to a Rails plugin.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=nwlm_C67dI8:B9MtdUJoCvk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=nwlm_C67dI8:B9MtdUJoCvk:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=nwlm_C67dI8:B9MtdUJoCvk:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=nwlm_C67dI8:B9MtdUJoCvk:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=nwlm_C67dI8:B9MtdUJoCvk:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=nwlm_C67dI8:B9MtdUJoCvk:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=nwlm_C67dI8:B9MtdUJoCvk:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=nwlm_C67dI8:B9MtdUJoCvk:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/nwlm_C67dI8" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/updating-a-single-record-attribute-with-activerecord.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/update-to-unobtrusive-javascript-plugin.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/K_4E826yJ_8/update-to-unobtrusive-javascript-plugin.html" rel="alternate" />
    <published>2006-06-07T14:33:00Z</published>
    <updated>2009-02-15T10:04:52Z</updated>
    <title>Update to Unobtrusive Javascript Plugin</title>
    <content type="html">&lt;div class="notice"&gt;Update 15 Feb 2009: The UJS plugin for Rails is no longer actively maintained but you can find the &lt;a href="http://github.com/lukeredpath/ujs4rails"&gt;the source on github&lt;/a&gt;.&lt;/div&gt;


&lt;p&gt;Some small changes and one major bug fix have been checked into Subversion today:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The controller action that serves up event-selector.js, eliminating the need to manually copy it to your public/javascripts folder, was looking for the js file in the wrong place, stopping the whole plugin from working. If you've already discovered this, &lt;strong&gt;please run an update&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;There is now an unobtrusive version of the link_to_remote function - this also dynamically appends a return false to the onclick event to ensure it works in Safari (see &lt;a href="http://particletree.com/notebook/eventstop/"&gt;this blog post&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;The rules are now assigned using an addLoadEvent function instead of window.onload - this is more of a quick fix and will be improved in the future (I plan to use Prototype's onReady extension) but in the meantime this makes sure it doesn't mess up any other onload events you might have.&lt;/li&gt;
&lt;li&gt;The rules are re-applied automatically on completion of any AJAX request&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Please post any bugs to the &lt;a href="http://opensource.agileevolved.com/"&gt;Agile Evolved Open Source Trac&lt;/a&gt;.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=K_4E826yJ_8:NXeq3bG-VB4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=K_4E826yJ_8:NXeq3bG-VB4:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=K_4E826yJ_8:NXeq3bG-VB4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=K_4E826yJ_8:NXeq3bG-VB4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=K_4E826yJ_8:NXeq3bG-VB4:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=K_4E826yJ_8:NXeq3bG-VB4:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=K_4E826yJ_8:NXeq3bG-VB4:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=K_4E826yJ_8:NXeq3bG-VB4:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/K_4E826yJ_8" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/update-to-unobtrusive-javascript-plugin.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/introducing-unobtrusive-javascript-for-rails.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/_j7Px6nSqzM/introducing-unobtrusive-javascript-for-rails.html" rel="alternate" />
    <published>2006-06-06T22:23:00Z</published>
    <updated>2009-02-15T10:04:52Z</updated>
    <title>Introducing Unobtrusive Javascript for Rails</title>
    <content type="html">&lt;div class="notice"&gt;Update 15 Feb 2009: The UJS plugin for Rails is no longer actively maintained but you can find the &lt;a href="http://github.com/lukeredpath/ujs4rails"&gt;the source on github&lt;/a&gt;.&lt;/div&gt;


&lt;p&gt;Rails makes a lot of things easier for a developer. One of those things is AJAX. The built-in Javascript and AJAX helpers, such as form_remote_tag and link_to_remote, make developing AJAX apps a breeze. But if there has every been one major bone of contention with these helpers, it's the markup they produce. Developers have been well aware of the need to separate content from presentation with CSS for a while now - less prevalent is the recognition of the benefits in separating &lt;em&gt;behaviour&lt;/em&gt; from content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update 21/08/2006&lt;/strong&gt;: The latest version of this plugin is 0.3 - please see &lt;a href="http://www.lukeredpath.co.uk/2006/8/21/ujs-rails-plugin-0-3-new-name-new-website"&gt;this post&lt;/a&gt; and the &lt;a href="http://www.ujs4rails.com"&gt;official UJS website&lt;/a&gt; for more information.&lt;/p&gt;

&lt;p&gt;Rails makes a lot of things easier for a developer. One of those things is AJAX. The built-in Javascript and AJAX helpers, such as form_remote_tag and link_to_remote, make developing AJAX apps a breeze. But if there has every been one major bone of contention with these helpers, it's the markup they produce. Developers have been well aware of the need to separate content from presentation with CSS for a while now - less prevalent is the recognition of the benefits in separating &lt;em&gt;behaviour&lt;/em&gt; from content.&lt;/p&gt;

&lt;p&gt;I'm not going to cover the ins and outs of why you should separate behaviour from content here, but or those unfamiliar with the reasons and benefits, Peter-Paul Koch of &lt;a href="http://www.quirksmode.org"&gt;QuirksMode&lt;/a&gt;   published a &lt;a href="http://www.digital-web.com/articles/separating_behavior_and_presentation/"&gt;great article&lt;/a&gt;  in 2004 highlighting the benefits of separating behaviour from content. More recently, Dan Webb from &lt;a href="http://www.vivabit.com"&gt;Vivabit&lt;/a&gt; published &lt;a href="http://www.vivabit.com/bollocks/2006/02/09/rails-is-the-devil-in-your-client-side-shoulder"&gt;an article about the problem with Rails AJAX helpers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As Dan points out in his article, the subject has been mentioned on &lt;a href="http://thread.gmane.org/gmane.comp.lang.ruby.rails/15668"&gt;several&lt;/a&gt; &lt;a href="http://lists.rubyonrails.org/pipermail/rails-spinoffs/2005-June/000025.html"&gt;occasions&lt;/a&gt; on the RubyOnRails mailing list but has often been shot down for a multitude of reasons ranging from "its not worth it" to "it would be too awkward". Invitations to submit patches were made but nothing came about.&lt;/p&gt;

&lt;p&gt;So I'd like to present to you something I've been working on here at &lt;a href="http://www.agileevolved.com"&gt;Agile Evolved&lt;/a&gt; - &lt;strong&gt;&lt;a href="http://opensource.agileevolved.com/trac/wiki/UnobtrusiveJavascript"&gt;Unobtrusive Javascript for Rails&lt;/a&gt;&lt;/strong&gt;. The plugin makes use of a Javascript library called &lt;a href="http://encytemedia.com/event-selectors/"&gt;event:Selectors&lt;/a&gt; by Justin Palmer at EncyteMedia. Similar to Ben Nolan's &lt;a href="http://bennolan.com/behaviour/"&gt;behaviour.js library&lt;/a&gt; but making full use of &lt;a href="http://prototype.conio.net/"&gt;Prototype&lt;/a&gt;, it allows you to use CSS selectors to attach Javascript events to your page. This plugin allows you to make use of the event:Selectors library, but in Ruby, directly from your controller or view and have the resulting behaviour rules dynamically generated at runtime &lt;em&gt;in an external javascript file&lt;/em&gt;. Let me demonstrate:&lt;/p&gt;

&lt;p&gt;First of all, you'll need to grab the plugin from the &lt;a href="http://opensource.agileevolved.com/svn/root/rails_plugins/unobtrusive_javascript/trunk/"&gt;Subversion repository&lt;/a&gt;. Please note - the plugin is in its early stages at the moment and is likely to be updated quite a bit over the coming weeks - I do not recommend using this in a production site just yet. In the meantime, if you want to have a play I recommend using svn:externals to make sure you keep your copy of the plugin up-to-date.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ ./script/plugin install -x http://source.ujs4rails.com/current/unobtrusive_javascript
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Next, you'll need to load in the Prototype javascript and the Unobtrusive Javascript scripts. Somewhere between your layout's head tags, add the following:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;%= javascript_include_tag :defaults %&amp;gt;
&amp;lt;%= unobtrusive_javascript_files %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can now attach events to elements in your page from either your controller or your view, using the register_js_behaviour() function. The function takes two parameters; the first is the CSS selector and event (for more details see the &lt;a href="http://encytemedia.com/event-selectors/"&gt;event:Selectors documentation&lt;/a&gt;) to attach your behaviour to and the second is a string of Javascript that you want to execute. Here's a small example:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;% register_js_behaviour "#my_funky_link:click", "alert('Hello World')" %&amp;amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Of course, writing out large strings of javascript could become cumbersome. The first solution is use the &lt;a href="http://api.rubyonrails.org/classes/ActionView/Helpers/PrototypeHelper/JavaScriptGenerator/GeneratorMethods.html"&gt;built-in Rails helpers&lt;/a&gt; to generate your Javascript strings. For instance, if we want to highlight a div when we hover over it with the mouse, you could do the following:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;% register_js_behaviour "#my_funky_div:mouseover",
        visual_effect(:highlight, "my_other_div", :duration =&amp;amp;gt; 0.5) %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, if you load up a page with one of the above calls and View Source, you might expect to see a whole load of generated Javascript. Except you won't - thats because the unobtrusive_javascript plugin makes use of a special controller and view to dynamically generate your behaviour rules at runtime, which are then linked to using a normal script tag. That means you can attach as many behaviours to your page as you like, from anywhere in your view or controller, &lt;em&gt;without&lt;/em&gt; clogging up your rendered HTML with Javascript.&lt;/p&gt;

&lt;p&gt;Next, there is the issue of graceful degradation. The two primary candidates for graceful degradation are the link_to_remote and form_remote_tag helpers - links and forms both have natural fallbacks - the HREF and ACTION respectively, and by using some unobtrusive Javascript we can make sure those fallbacks are used when somebody has Javascript disabled.&lt;/p&gt;

&lt;p&gt;This initial release contains an updated version of the form_remote_tag helper - by default it does exactly the same thing as the built-in Rails helper but making it use unobtrusive Javascript is a case of one small addition to your code:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;%= form_remote_tag :url =&amp;gt; { :action =&amp;gt; 'foo' }, :unobtrusive =&amp;gt; true %
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Because the event:Selector library depends on an HTML ID to attach an event, your forms will need an ID but worry not - another modification to the form_remote_tag helper will mean that if you do not specify your own HTML ID, one will be automatically generated for you.&lt;/p&gt;

&lt;p&gt;Finally, some of you may be aware that Dan Webb, who I mentioned earlier in this article, has been working on his own &lt;a href="http://svn.vivabit.net/external/rubylibs/unobtrusive_javascript/"&gt;unobtrusive javascript plugin&lt;/a&gt;. I have been in touch with Dan and he preferred the simpler syntax that my plugin uses. That said, he's plugin contains some very cool stuff and we've agreed to merge the best of both of our plugins together over the coming weeks, including the ability to supply the register_\javascript_behaviour() function with a block which will let you write the Javascript functionality you want to attach using Dan's RJS-style JavascriptProxy classes.&lt;/p&gt;

&lt;p&gt;Please do download the plugin and have a play around. You can report any bugs on the &lt;a href="http://opensource.agileevolved.com"&gt;Agile Evolved Open Source Software website&lt;/a&gt; and any opinions and ideas are welcomed.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=_j7Px6nSqzM:odXA2KB424k:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=_j7Px6nSqzM:odXA2KB424k:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=_j7Px6nSqzM:odXA2KB424k:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=_j7Px6nSqzM:odXA2KB424k:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=_j7Px6nSqzM:odXA2KB424k:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=_j7Px6nSqzM:odXA2KB424k:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=_j7Px6nSqzM:odXA2KB424k:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=_j7Px6nSqzM:odXA2KB424k:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/_j7Px6nSqzM" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/introducing-unobtrusive-javascript-for-rails.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/unobtrusive-javascript-in-railswhat-if.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/7yGj9OgMKiM/unobtrusive-javascript-in-railswhat-if.html" rel="alternate" />
    <published>2006-06-06T00:22:00Z</published>
    <updated>2009-02-15T10:04:52Z</updated>
    <title>Unobtrusive Javascript in Rails...what if?</title>
    <content type="html">&lt;div class="notice"&gt;Update 15 Feb 2009: The UJS plugin for Rails is no longer actively maintained but you can find the &lt;a href="http://github.com/lukeredpath/ujs4rails"&gt;the source on github&lt;/a&gt;.&lt;/div&gt;


&lt;p&gt;What if...you could produce accessible, unobtrusive javascript, using Rails built-in javascript/prototype helpers, with just one extra line of code in your layout, a plugin, and one small enhancement to the helpers? Something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;% form_remote_tag :controller =&amp;gt; 'foo', :action =&amp;gt; 'bar', :unobtrusive =&amp;gt; true %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Which produces:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;form id="form_foo_bar" action="/foo/bar" method="post"&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;But which still acts as an ajax form? You would simply handle the response for ajax and non ajax requests using Rails' responds_to function.&lt;/p&gt;

&lt;p&gt;In addition, what if you could attach javascript functionality to your page elements in a Behaviour-like fashion, but using pure Ruby, anywhere in your view, but loaded from an external Javascript file?&lt;/p&gt;

&lt;p&gt;Sorry to tease. Stay tuned, we have something very cool to show off in the next day or two.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://opensource.agileevolved.com" title="Agile Evolved Open Source Software"&gt;http://opensource.agileevolved.com&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=7yGj9OgMKiM:chr1PJ8Pgnw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=7yGj9OgMKiM:chr1PJ8Pgnw:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=7yGj9OgMKiM:chr1PJ8Pgnw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=7yGj9OgMKiM:chr1PJ8Pgnw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=7yGj9OgMKiM:chr1PJ8Pgnw:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=7yGj9OgMKiM:chr1PJ8Pgnw:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=7yGj9OgMKiM:chr1PJ8Pgnw:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=7yGj9OgMKiM:chr1PJ8Pgnw:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/7yGj9OgMKiM" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/unobtrusive-javascript-in-railswhat-if.html</feedburner:origLink></entry>
  <entry>
    <id>http://lukeredpath.co.uk/bloglukeredpath.co.uk/a-brief-introduction.html</id>
    <link type="text/html" href="http://feedproxy.google.com/~r/LukeRedpath/~3/aOfcymy_58M/a-brief-introduction.html" rel="alternate" />
    <published>2006-04-05T13:24:00Z</published>
    <updated>2009-02-15T08:14:32Z</updated>
    <title>A Brief Introduction</title>
    <content type="html">&lt;p&gt;I'm not great at introductions so I'll try and keep this short and sweet. My name's Luke Redpath and I'm a developer from London, UK. I'm the Senior Software Engineer at &lt;a href="http://www.agileevolved.com"&gt;Agile Evolved&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As part of my job I get to work almost exclusively with RubyOnRails, something I will post about frequently on my blog. I'll be sharing Rails tips and opinions and we have some interesting Rails plugins under development - more about that soon.&lt;/p&gt;

&lt;p&gt;I'm also a &lt;a href="http://www.sitepoint.com/forums"&gt;SitePoint&lt;/a&gt; mentor and you can find me posting frequently in the Ruby, PHP Application Design and General Development forums.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=aOfcymy_58M:kJjRNdiOpHQ:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=aOfcymy_58M:kJjRNdiOpHQ:63t7Ie-LG7Y"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?d=63t7Ie-LG7Y" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=aOfcymy_58M:kJjRNdiOpHQ:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=aOfcymy_58M:kJjRNdiOpHQ:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=aOfcymy_58M:kJjRNdiOpHQ:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=aOfcymy_58M:kJjRNdiOpHQ:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/LukeRedpath?a=aOfcymy_58M:kJjRNdiOpHQ:xy14PzhHsPU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/LukeRedpath?i=aOfcymy_58M:kJjRNdiOpHQ:xy14PzhHsPU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/LukeRedpath/~4/aOfcymy_58M" height="1" width="1"/&gt;</content>
    <author>
      <name>Luke Redpath</name>
      <email>luke@lukeredpath.co.uk</email>
    </author>
  <feedburner:origLink>http://lukeredpath.co.uk/blog/a-brief-introduction.html</feedburner:origLink></entry>
</feed>
