<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/rss2full.xsl" type="text/xsl" media="screen"?><?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/itemcontent.css" type="text/css" media="screen"?><rss version="2.0"><channel><description>Tim Riley is a web and Ruby on Rails developer based in Canberra, Australia.</description><title>Blah Blah Woof Woof</title><generator>Tumblr (3.0; @timriley)</generator><link>http://log.openmonkey.com/</link><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/BlahBlahWoofWoof" type="application/rss+xml" /><item><title>Code for Christmas</title><description>&lt;a href="http://rhnh.net/2008/12/01/code-for-christmas"&gt;Code for Christmas&lt;/a&gt;: Xavier Shay makes a pretty compelling suggestion: developers are always time-poor, so why not give the best Christmas present possible and contribute a little of your time to their project?</description><link>http://log.openmonkey.com/post/62403301</link><guid>http://log.openmonkey.com/post/62403301</guid><pubDate>Mon, 01 Dec 2008 22:12:39 +1100</pubDate></item><item><title>Capistrano task to selectively update crontabs</title><description>&lt;p&gt;In a lot of our Rails projects, we need to throw a few entries into a user’s crontab, for things like scheduled rake tasks or thinking sphinx updates.&lt;/p&gt;

&lt;p&gt;However, we don’t want to overwrite the entire crontab file whenever we want to add new entries or change the details of the existing ones.&lt;/p&gt;

&lt;p&gt;Thus, I’ve devised a little technique to allow selective updates of the crontab. Take this demo and apply it where necessary in your capistrano deploy.rb file:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
namespace :my_rake_task do
  task :add_cron_job, :roles =&gt; :app, :except =&gt; { :no_release =&gt; true } do
    tmpname = "/tmp/appname-crontab.#{Time.now.strftime('%s')}"
    # run crontab -l or echo '' instead because the crontab command will fail if the user has no pre-existing crontab file.
    # in this case, echo '' is run and the cap recipe won't fail altogether.
    run "(crontab -l || echo '') | grep -v 'rake my_rake_task' &gt; #{tmpname}"
    run "echo '12 1,11,18 * * * cd #{current_path} &amp;&amp; RAILS_ENV=production rake my_rake_task' &gt;&gt; #{tmpname}"
    run "crontab #{tmpname}"
    run "rm #{tmpname}"
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To step you through it, this task will dump the current crontab to a file, excluding the command that you care about, then add the command back to the bottom of the file, and install the crontab again, with all the other contents preserved. This will allow you to change the particular entry without clobbering the entire file. Nifty!&lt;/p&gt;</description><link>http://log.openmonkey.com/post/61404666</link><guid>http://log.openmonkey.com/post/61404666</guid><pubDate>Tue, 25 Nov 2008 12:56:38 +1100</pubDate><category>rails</category></item><item><title>Using Markov Chains to provide English language seed data for your Rails application</title><description>&lt;p&gt;I spent a portion of last weekend’s Rails Rumble preparing a script that would seed our application’s database with test data. Among other things, having a well populated database is useful to fully test all the parts of the application’s interface that might not come into play when using a smaller data set. It also gives you the ability to get a true sense of how your app will look and feel after the real users start to pour in content (hopefully!).&lt;/p&gt;

&lt;p&gt;For the Rumble project, I used the &lt;a href="http://faker.rubyforge.org/"&gt;faker&lt;/a&gt; Ruby gem, which provides methods for generating realistic names, domains and email addresses. It also provides a text generator that pulls random strings from Lorem Ipsum. However, while the app may feel have felt fully populated, seeing the Latin everywhere didn’t make it feel &lt;em&gt;right&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So for the next project, I will generate random English language strings for the seed data. Rather than pulling words ad hoc from a dictionary or something, we can use &lt;a href="http://en.wikipedia.org/wiki/Markov_chain"&gt;Markov Chains&lt;/a&gt; to generate reasonably realistically structured text for us. A bit of searching revealed a &lt;a href="http://rubyquiz.com/quiz74.html"&gt;Ruby Quiz page&lt;/a&gt; with an implementation of a text generator using Markov Chains. It explains that the generator:&lt;/p&gt;

&lt;blockquote&gt;read[s] some text document(s), making note of which characters commonly follow which characters or which words commonly follow other words (it works for either scale). Then, when generating text, you just select a character or word to output, based on the characters or words that came before it.&lt;/blockquote&gt;
  
&lt;p&gt;I took implementation provided on this page, and added a couple of convenience methods to easily fetch sequences of words or whole sentences:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# Courtesy of &lt;a href="http://rubyquiz.com/quiz74.html"&gt;http://rubyquiz.com/quiz74.html&lt;/a&gt;

class MarkovChain
  def initialize(text)
    @words = Hash.new
    wordlist = text.split
    wordlist.each_with_index do |word, index|
    add(word, wordlist[index + 1]) if index = random
    end.first
    next_word
  end
  
  # Convenience methods to easily access words and sentences
  
  def random_word 
    @words.keys.rand
  end
  
  def words(count = 1, start_word = nil)
    sentence  = ''
    word      = start_word || random_word
    count.times do
      sentence &lt;&lt; word &lt;&lt; ' '
      word = get(word)
    end
    sentence.strip.gsub(/[^A-Za-z\s]/, '')
  end
  
  def sentences(count = 1, start_word = nil)
    word      = start_word || random_word
    sentences = ''
    until sentences.count('.') == count
      sentences &lt;&lt; word &lt;&lt; ' '
      word = get(word)
    end
    sentences
  end
end&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then, in my seed data script, I prime a MarkovChain instance with some good literature (I chose Sir Arthur Conan Doyle’s &lt;em&gt;&lt;a href="http://gutenberg.org/etext/139"&gt;The Lost World&lt;/a&gt;&lt;/em&gt; from Project Gutenburg), and then use it to populate my records:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;mc = MarkovChain.new(File.read("#{RAILS_ROOT}/db/populate_source.txt"))

# create_or_update method taken from &lt;a href="http://railspikes.com/2008/2/1/loading-seed-data"&gt;http://railspikes.com/2008/2/1/loading-seed-data&lt;/a&gt;

1.upto(100) do |comment_id|
  Comment.create_or_update(
    :id =&gt; comment_id,
    :title =&gt; mc.words(3).titleize,
    :body =&gt; mc.sentences(3)
  )
end&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Done! While the text isn’t the kind of fluent prose you’d expect from the real-life internet commenters, it does look much more realistic than Lorem Ipsum, and often provides cause for a bit of a chuckle. An example of the kind of stuff you’ll get from The Lost World is below. Feel free to choose your favourite text as the source :)&lt;/p&gt;

&lt;blockquote&gt;Luxurious voyage I had a solemnity as one would no sneer would have just after two surviving Indians in a precipice, and were accompanying to his thumb over his shoulders, the effort.&lt;/blockquote&gt;

&lt;p&gt;For some background on how to load seed data into your Rails app, see &lt;a href="http://railspikes.com/2008/2/1/loading-seed-data"&gt;this page&lt;/a&gt; on the Rail Spikes blog for a good introduction.&lt;/p&gt;</description><link>http://log.openmonkey.com/post/55783578</link><guid>http://log.openmonkey.com/post/55783578</guid><pubDate>Thu, 23 Oct 2008 00:41:00 +1100</pubDate><category>rails</category></item><item><title>For the win!</title><description>&lt;p&gt;In the last 48 hours, two friends and colleagues and I laboured over a brand new web app, for our entry in this year’s &lt;a title="Rails Rumble" href="http://railsrumble.com/"&gt;Rails Rumble&lt;/a&gt;. The result is something we’re proud to share with you: &lt;b&gt;&lt;a title="Ideas FTW" href="http://ideasftw.com/"&gt;Ideas FTW!&lt;/a&gt;&lt;/b&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://ideasftw.com/" title="Ideas FTW!"&gt;&lt;img height="313" width="500" alt="Screenshot of Ideas FTW" src="http://farm4.static.flickr.com/3285/2957233487_26d7d58e90.jpg" align="middle" border="0"/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We all have plenty of ideas, most of which we never have the opportunity to act on. Ideas FTW is an idea &lt;i&gt;incubator&lt;/i&gt; that allows you to see your ideas come to life. You can vote on ideas, post your own, take up an idea and start an effort to achieve it on your own or in a group.&lt;/p&gt;
&lt;p&gt;The site is simple and colourful, and will only become more fun to use as more ideas get added. So, please do sign up with your &lt;a title="OpenID" href="http://openid.net/"&gt;OpenID&lt;/a&gt; and empty your overflowing brains! I’m looking forward to seeing some interesting ideas fill the site.&lt;/p&gt;
&lt;p&gt;Of course, the app is far from perfect yet. A few bugs slipped through and there are certainly a couple more key features we would like to add. If you have any suggestions for improving Ideas FTW, please feel free to &lt;a title="Ideas FTW effort page" href="http://ideasftw.com/efforts/1"&gt;leave a comment on our effort page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a title="Hugh Evans" href="http://hughevans.net/"&gt;Hugh&lt;/a&gt;, &lt;a title="Michael MacDonald" href="http://starclass.com.au/"&gt;Michael&lt;/a&gt; and I all had a great time working together to create this site. We learnt a great many new things over the weekend and I am sure that I can speak for the others to say the lack of sleep was worth it. If you enjoy the site, please feel free to &lt;a title="Rails Rumble team page" href="http://railsrumble.com/teams/all-caps"&gt;vote for us&lt;/a&gt; when the tallies open in a couple of days.&lt;/p&gt;</description><link>http://log.openmonkey.com/post/55414413</link><guid>http://log.openmonkey.com/post/55414413</guid><pubDate>Mon, 20 Oct 2008 20:51:00 +1100</pubDate></item><item><title>Configuring god to monitor Sphinx's searchd</title><description>&lt;p&gt;&lt;a href="http://god.rubyforge.org/"&gt;God&lt;/a&gt; is a server monitoring tool helpful for starting your server processes and keeping them running, all the while making sure they don’t misbehave. In the Rails world, it appears to be used most commonly with packs of mongrels or thins. However, this is not to say it can’t be used to monitor other software. For one of my recent work projects, we’ve been using God to monitor the searchd process that &lt;a href="http://sphinxsearch.com/"&gt;Sphinx&lt;/a&gt; uses to serve results to search queries.&lt;/p&gt;

&lt;p&gt;The configuration required for this can be based mostly on what you see around the place for thins or mongrels:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
require 'yaml'

app_config = YAML.load(File.open(File.dirname(__FILE__) + "/config.yml"))['production']

God.watch do |w|
  w.group = "#{app_config['app_name']}-sphinx"
  w.name  = w.group + "-1"
  
  w.interval = 30.seconds
  
  w.uid = app_config['user']
  w.gid = app_config['group']
  
  w.start         = "searchd --config #{app_config['app_root']}/config/sphinx.conf"
  w.start_grace   = 10.seconds  
  w.stop          = "searchd --config #{app_config['app_root']}/config/sphinx.conf --stop"
  w.stop_grace    = 10.seconds  
  w.restart       = w.stop + " &amp;&amp; " + w.start
  w.restart_grace = 15.seconds
  
  w.pid_file = File.join(app_config['app_root'], "tmp/pids/sphinx.pid")
  
  w.behavior(:clean_pid_file)
  
  w.start_if do |start|
    start.condition(:process_running) do |c|
      c.interval  = 5.seconds
      c.running   = false
    end
  end
  
  w.restart_if do |restart|
    restart.condition(:memory_usage) do |c|
      c.above = 100.megabytes
      c.times = [3, 5] # 3 out of 5 intervals
    end
  end
  
  w.lifecycle do |on|
    on.condition(:flapping) do |c|
      c.to_state      = [:start, :restart]
      c.times         = 5
      c.within        = 5.minutes
      c.transition    = :unmonitored
      c.retry_in      = 10.minutes
      c.retry_times   = 5
      c.retry_within  = 2.hours
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;There are a number of other changes required for this configuration to work properly. Firstly, you will require a config/config.yml file, containing something like the following:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
development: &amp;production_settings
  app_name: myapp
  user: deployer
  group: deployer
  app_root: "/home/deployer/deployments/myapp.amc.org.au/current"
test:
  &lt;&lt;: *production_settings
production:
  &lt;&lt;: *production_settings
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This config file sets the app’s short name, the user and group to run the searchd process, and the path to the rails application root. We keep these settings in a yaml file because they are also used in a number of other places besides here. If you don’t have this requirement, you can put all of these settings directly into your god config file.&lt;/p&gt;

&lt;p&gt;The other requirement is that we make Sphinx use a configuration file that has a predictable name. The default Sphinx config file, when using &lt;a href="http://freelancing-gods.com/"&gt;Pat Allen’s&lt;/a&gt; awesome &lt;a href="http://ts.freelancing-gods.com/"&gt;Thinking Sphinx&lt;/a&gt; plugin, has a file name contains the name of the current Rails environment. God can’t use this to launch searchd, because it is does not run within the context of the Rails environment. To set location and name of the sphinx configuration file, set up a config/sphinx.yml file that contains something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
development: &amp;production_settings
  config_file: /home/deployer/deployments/myapp.amc.org.au/current/config/sphinx.conf
  pid_file: /home/deployer/deployments/myapp.amc.org.au/current/tmp/pids/sphinx.pid
test:
  &lt;&lt;: *production_settings
production:
  &lt;&lt;: *production_settings
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Thinking Sphinx respects these settings and uses them when it generates the Sphinx config file.&lt;/p&gt;

&lt;p&gt;So that’s about it. Make sure you’ve run `rake thinking_sphinx:index` at least once in your Rails app, to generate the Sphinx configuration and indexes, and then you are ready to start god and have your searchd automatically started and monitored!&lt;/p&gt;</description><link>http://log.openmonkey.com/post/49556051</link><guid>http://log.openmonkey.com/post/49556051</guid><pubDate>Wed, 10 Sep 2008 23:27:00 +1000</pubDate><category>rails</category><category>sphinx</category><category>linux</category><category>amc</category></item><item><title>Lessons learnt the hard way: Don't use script/console --sandbox on production apps</title><description>&lt;p&gt;Don’t use &lt;i&gt;script/console -s&lt;/i&gt; or &lt;i&gt;script/console —sandbox&lt;/i&gt; on your live, running Rails application. The built-in transactions that roll back on exit are nifty, but if your database’s transactions use any kind of locking, then you will get locks on the tables for any models that you use in the console. This will most likely stop your application from working.&lt;/p&gt;
&lt;p&gt;Moral of the story? If you need to use the console on a live application, just be careful. You’re smart, you’re cautious, you don’t need the sandbox. It will only cause more trouble than it is worth.&lt;/p&gt;</description><link>http://log.openmonkey.com/post/49496179</link><guid>http://log.openmonkey.com/post/49496179</guid><pubDate>Wed, 10 Sep 2008 12:33:00 +1000</pubDate><category>rails</category></item><item><title>"It’s one of the biggest challenges in the digital age: When you can bombard people with everything,..."</title><description>“It’s one of the biggest challenges in the digital age: When you can bombard people with everything, it’s tempting to do so. That’s why taste, restraint, and editing are so important. Sometimes it’s about throwing out the 35 bad shots and revelling in the one great shot.”&lt;br/&gt;&lt;br/&gt; - &lt;em&gt;Capture attention with only the &lt;a href="http://www.37signals.com/svn/posts/1228-a-361-ratio-is-actually-pretty-good"&gt;cream of your crop&lt;/a&gt;.&lt;/em&gt;</description><link>http://log.openmonkey.com/post/48969212</link><guid>http://log.openmonkey.com/post/48969212</guid><pubDate>Sat, 06 Sep 2008 16:47:06 +1000</pubDate></item><item><title>I made tea.</title><description>&lt;a href="http://www.telescopictext.com/"&gt;I made tea.&lt;/a&gt;: Fascinating, infuriating, and addictive. Great concept. Via &lt;a href="http://log.morrisonfilm.com/post/46601006"&gt;MorrisonFilm&lt;/a&gt;.</description><link>http://log.openmonkey.com/post/47424515</link><guid>http://log.openmonkey.com/post/47424515</guid><pubDate>Tue, 26 Aug 2008 17:04:12 +1000</pubDate></item><item><title>"The most important file, as we all know, is application_helper.rb, because this is where code goes..."</title><description>“The most important file, as we all know, is application_helper.rb, because this is where code goes to die. It’s often a few hundred lines of randomly added, unrelated methods. This is a confusing, scary place for methods to be.”&lt;br/&gt;&lt;br/&gt; - &lt;em&gt;Eric Chapweske on &lt;a href="http://railspikes.com/2008/8/22/how-to-fix-your-rails-helpers"&gt;how to fix your rails helpers&lt;/a&gt;.&lt;/em&gt;</description><link>http://log.openmonkey.com/post/47287046</link><guid>http://log.openmonkey.com/post/47287046</guid><pubDate>Mon, 25 Aug 2008 17:31:25 +1000</pubDate><category>rails</category></item><item><title>Beijing Olympic medal tally for our Campfire bot</title><description>&lt;p&gt;I’ve recently been working on a bot framework for 37signals’ &lt;a href="http://campfirenow.com/"&gt;Campfire&lt;/a&gt; web chat system. We use Campfire at work and it’s been great for having fun together and  increasing team cohesiveness.&lt;/p&gt;

&lt;p&gt;Having a bot in the room is also good for random quotes, Chuck Norris facts and comic strips. We also plan to add a few useful work-related things, such as git &amp; subversion commit messages and server monitoring alerts.&lt;/p&gt;

&lt;p&gt;The bot is plugin-oriented and easy to extend. Check out the &lt;a href="http://github.com/timriley/campfire-bot/tree/master"&gt;code on github&lt;/a&gt;. It’s still in flux and I would love to hear any feedback.&lt;/p&gt;

&lt;p&gt;This afternoon I hacked together a little plugin to report the Beijing olympics medal tally, thanks to an idea of Sean’s. The code is highly specific, but it shows how easy (and fun!) it is to add a command to the bot and scrape data off the web using hpricot.&lt;/p&gt;

&lt;p&gt;Here it is:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;require 'open-uri'
require 'hpricot'

class BeijingTally &lt; PluginBase
  
  on_command 'tally', :tally
  
  def tally(msg)
    output    = "#{'Pos.'.rjust(6)} - #{'Country'.ljust(25)} -   G -   S -   B - Total\n"
    rows      = ((Hpricot(open('http://results.beijing2008.cn/WRM/ENG/INF/GL/95A/GL0000000.shtml'))/'//table')[1]/'tr')[2..-1]
    rows.each_with_index do |row, i|
      cells   = row/'td'
      output += "#{strip_tags_or_zero(cells[0].inner_html).rjust(6)} - " # position
      output += "#{((i == rows.length - 1) ? '' : strip_tags_or_zero(cells[1].inner_html)).ljust(25)} - " # country
      output += "#{strip_tags_or_zero(cells[-5].inner_html).rjust(3)} - " # gold
      output += "#{strip_tags_or_zero(cells[-4].inner_html).rjust(3)} - " # silver
      output += "#{strip_tags_or_zero(cells[-3].inner_html).rjust(3)} - " # bronze
      output += "#{strip_tags_or_zero(cells[-2].inner_html).rjust(3)}\n"  # total
    end
    
    paste(output)
  end
  
  private
  
  # Take away the HTML tags from the string and insert a '0' if it is empty
  def strip_tags_or_zero(str)
    (out = str.gsub(/]*&gt;/, "").strip).blank? ? '0' : out
  end
end&lt;/code&gt;&lt;/pre&gt;</description><link>http://log.openmonkey.com/post/45395898</link><guid>http://log.openmonkey.com/post/45395898</guid><pubDate>Sun, 10 Aug 2008 18:28:00 +1000</pubDate><category>ruby</category><category>amc</category></item><item><title>"Few things are better than spending time in a creative haze, consumed by ideas, watching your work..."</title><description>“Few things are better than spending time in a creative haze, consumed by ideas, watching your work come to life, going to bed eager to wake up quickly and go try things out. I am not suggesting that excessive hours are needed or even advisable; a sane schedule is a must except for occasional binges. The point is that programming is an intense creative pleasure, a perfect mixture of puzzles, writing, and craftsmanship.”&lt;br/&gt;&lt;br/&gt; - &lt;em&gt;Lucky to be a programmer indeed. From &lt;a href="http://duartes.org/gustavo/blog/post/lucky-to-be-a-programmer"&gt;Gustavo Duarte&lt;/a&gt;, via &lt;a href="http://gusmueller.com/blog/archives/2008/07/lucky_to_be_a_programmer.html"&gt;Gus Mueller&lt;/a&gt;.&lt;/em&gt;</description><link>http://log.openmonkey.com/post/43184139</link><guid>http://log.openmonkey.com/post/43184139</guid><pubDate>Wed, 23 Jul 2008 09:34:00 +1000</pubDate></item><item><title>tuneage:


Lilofee - “Lock and Key”

We don’t know much about...</title><description>&lt;embed type="application/x-shockwave-flash" src="http://log.openmonkey.com/swf/audio_player.swf?audio_file=http://www.tumblr.com/audio_file/41153896/uuF4wghRKb0bu4fojgKmqUtP&amp;color=FFFFFF" height="27" width="207" quality="best"&gt;&lt;/embed&gt;&lt;br/&gt;&lt;br/&gt;&lt;p&gt;&lt;a href="http://tuneage.tumblr.com/post/40960802/lilofee-lock-and-key-we-dont-know-much"&gt;tuneage&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="http://myspace.com/lilofeeband"&gt;Lilofee&lt;/a&gt; - “Lock and Key”&lt;/p&gt;

&lt;p&gt;We don’t know much about Lilofee, except that they make excellent electro pop, listing their influences as 60s girl bands, 80s dark pop, and 90s industrial.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Don’t just check out this sweet tune from lilofree, but also head over to &lt;a href="http://tuneage.tumblr.com/"&gt;tuneage&lt;/a&gt; for regular doses of new, interesting music!&lt;/p&gt;</description><link>http://log.openmonkey.com/post/41153896</link><guid>http://log.openmonkey.com/post/41153896</guid><pubDate>Sun, 06 Jul 2008 16:28:23 +1000</pubDate></item><item><title>Mum's Savoury Mince Pockets</title><description>&lt;p&gt;These guys are easy to make and taste excellent, and are also good cold or reheated the next day! Thanks, Mum.&lt;/p&gt;


&lt;ul&gt;
&lt;li&gt;1 kg good (beef) mince&lt;/li&gt;
&lt;li&gt;2 carrots, peeled and diced&lt;/li&gt;
&lt;li&gt;1 onion, diced
&lt;/li&gt;
&lt;li&gt;1 tsp garlic&lt;/li&gt;
&lt;li&gt;2 tsp curry powder&lt;/li&gt;
&lt;li&gt;1 cup frozen peas and corn&lt;/li&gt;
&lt;li&gt;Tomato sauce&lt;/li&gt;
&lt;li&gt;Sweet chilli sauce&lt;/li&gt;
&lt;li&gt;2 cups cooked rice&lt;/li&gt;
&lt;li&gt;Lebanese bread, around 3-4 pieces&lt;/li&gt;
&lt;ul&gt;&lt;p&gt;&lt;/p&gt;&lt;/ul&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;Fry the mince with the garlic and curry powder.&lt;/li&gt;
&lt;li&gt;Once the mince is getting close to done, add the peas and corn.&lt;/li&gt;
&lt;li&gt;Add tomato sauce and sweet chilli sauce to taste.&lt;/li&gt;
&lt;li&gt;Add the cooked rice and mix it all up.&lt;/li&gt;
&lt;li&gt;Cut the lebanese bread in half to form pockets and fill with the mince and rice mixture.&lt;/li&gt;
&lt;li&gt;Put the filled pockets in an oven at 180°C for 5-10 minutes, or until crisp.&lt;/li&gt;
&lt;/ol&gt;</description><link>http://log.openmonkey.com/post/40136464</link><guid>http://log.openmonkey.com/post/40136464</guid><pubDate>Sat, 28 Jun 2008 14:29:23 +1000</pubDate><category>recipes</category></item><item><title>Displaying both local and HTTP remote images in Prince XML generated PDFs</title><description>&lt;p&gt;In our Rails apps, we use the awesome &lt;a href="http://princexml.com/"&gt;Prince XML&lt;/a&gt; to generate PDFs. We interact with the prince command line application using the &lt;a href="http://sublog.subimage.com/articles/2007/05/29/html-css-to-pdf-using-ruby-on-rails"&gt;Ruby library and Rails helper&lt;/a&gt; from the guys over at subimage interactive.&lt;/p&gt;

&lt;p&gt;When using their helper to generate a PDF from a Rails template, all image tags have the src attribute altered so they point to paths that are relative to the local filesystem, not just the root of your application.&lt;/p&gt;

&lt;p&gt;However, this breaks any images that you are loading from remote locations over HTTP. For us, this ended up breaking the &lt;a href="http://code.google.com/apis/maps/documentation/staticmaps/"&gt;static Google Maps&lt;/a&gt; that we were generating.&lt;/p&gt;

&lt;p&gt;So here’s an updated make_pdf helper that only modifies the image paths if they are local. This lets us use both local and HTTP-hosted images on the same PDF!&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# We use this chunk of controller code all over to generate PDF files.
#
# To stay DRY we placed it here instead of repeating it all over the place.
#
module PdfHelper
  require 'prince'

  private
    # Makes a pdf, returns it as data...
    def make_pdf(template_path, pdf_name, landscape=false)
      prince = Prince.new()
      # Sets style sheets on PDF renderer.
      prince.add_style_sheets(
        "#{RAILS_ROOT}/public/stylesheets/application.css",
        "#{RAILS_ROOT}/public/stylesheets/print.css",
        "#{RAILS_ROOT}/public/stylesheets/prince.css"
      )
      prince.add_style_sheets("#{RAILS_ROOT}/public/stylesheets/prince_landscape.css") if landscape
      # Render the estimate to a big html string.
      # Set RAILS_ASSET_ID to blank string or rails appends some time after
      # to prevent file caching, and messing up local-disk requests.
      ENV["RAILS_ASSET_ID"] = ''
      html_string = render_to_string(:template =&gt; template_path, :layout =&gt; 'document')
      # Make all paths relative to the file systemm, but only if they don't have http(s):// at the start.
      html_string.gsub!(%r{(src=")([^h][^t][^t][^p][^s]?[^:][^/]*)}, "src=\"#{RAILS_ROOT}/public\\2")
      # Send the generated PDF file from our html string.
      return prince.pdf_from_string(html_string)
    end

    # Makes and sends a pdf to the browser
    #
    def make_and_send_pdf(template_path, pdf_name, landscape=false)
      send_data(
        make_pdf(template_path, pdf_name, landscape),
        :filename =&gt; pdf_name,
        :type =&gt; 'application/pdf'
      ) 
    end
end&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And just to be precise, here is the diff between the helpers:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;--- pdf_helper.rb	2008-06-27 15:05:44.000000000 +1000
+++ new_pdf_helper.rb	2008-06-27 15:05:54.000000000 +1000
@@ -18,11 +18,11 @@
       prince.add_style_sheets("#{RAILS_ROOT}/public/stylesheets/prince_landscape.css") if landscape
       # Render the estimate to a big html string.
       # Set RAILS_ASSET_ID to blank string or rails appends some time after
-      # to prevent file caching, fucking up local - disk requests.
+      # to prevent file caching, and messing up local-disk requests.
       ENV["RAILS_ASSET_ID"] = ''
       html_string = render_to_string(:template =&gt; template_path, :layout =&gt; 'document')
-      # Make all paths relative, on disk paths...
-      html_string.gsub!("src=\"", "src=\"#{RAILS_ROOT}/public")
+      # Make all paths relative to the file systemm, but only if they don't have http(s):// at the start.
+      html_string.gsub!(%r{(src=")([^h][^t][^t][^p][^s]?[^:][^/]*)}, "src=\"#{RAILS_ROOT}/public\\2")
       # Send the generated PDF file from our html string.
       return prince.pdf_from_string(html_string)
     end&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I’d also love it if you could propose a better way to handle the regular expression inside the gsub! call. Leave a comment!&lt;/p&gt;</description><link>http://log.openmonkey.com/post/40010879</link><guid>http://log.openmonkey.com/post/40010879</guid><pubDate>Fri, 27 Jun 2008 15:01:00 +1000</pubDate><category>rails</category></item><item><title>Very excited about my second trip to China that will come later...</title><description>&lt;img src="http://media.tumblr.com/qDLJTkQC9aoyczcgExqvDF9T_500.jpg"/&gt;&lt;br/&gt;&lt;br/&gt;Very excited about my second trip to China that will come later this year.</description><link>http://log.openmonkey.com/post/39921110</link><guid>http://log.openmonkey.com/post/39921110</guid><pubDate>Thu, 26 Jun 2008 23:40:00 +1000</pubDate></item><item><title>Loading the ActiveRecord SQL Server adapter in a Rails 2.1 app</title><description>It’s pretty simple. In your config/environment.rb:

&lt;pre&gt;&lt;code&gt;  config.gem 'activerecord-sqlserver-adapter', :source =&gt; 'http://gems.rubyonrails.org', :lib =&gt; 'active_record/connection_adapters/sqlserver_adapter'&lt;/code&gt;&lt;/pre&gt;

Then run rake gems:install and away you go.

Hugh has &lt;a href="http://hughevans.net/2008/05/25/rails-ubuntu-odbc"&gt;some more tips&lt;/a&gt; about the packages and configuration required to connect to an SQL Server from a Linux platform.</description><link>http://log.openmonkey.com/post/38836256</link><guid>http://log.openmonkey.com/post/38836256</guid><pubDate>Wed, 18 Jun 2008 14:01:00 +1000</pubDate><category>rails</category></item><item><title>Enabling a non-interactive install of Blackdown's j2re1.4 on Ubuntu or Debian</title><description>&lt;p&gt;When you `apt-get install` the j2re1.4 Java package in Debian or Ubuntu, it displays a few ncurses-style dialogs to configure the software and to require your acceptance of the license agreement. Working with these dialogs is fine if you are installing the software with apt-get in a typical interactive shell session. If you are installing without this capacity to interact (like in a script), you will run into problems. Here’s how to fix it.&lt;/p&gt;

&lt;p&gt;If you install the package with the noninteractive frontend for dpkg, then you’ll get an error like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# DEBIAN_FRONTEND=noninteractive /usr/bin/apt-get --yes --force-yes install j2re1.4


Reading package lists... Done
Building dependency tree       
Reading state information... Done
Suggested packages:
  mozilla-browser mozilla-firefox galeon ttf-kochi-gothic ttf-kochi-mincho
Recommended packages:
  gsfonts-x11 libx11-6 libxext6 libxi6
The following NEW packages will be installed:
  j2re1.4
0 upgraded, 1 newly installed, 0 to remove and 23 not upgraded.
Need to get 0B/22.5MB of archives.
After unpacking 60.3MB of additional disk space will be used.
Preconfiguring packages ...
j2re1.4 failed to preconfigure, with exit status 10
Selecting previously deselected package j2re1.4.
(Reading database ... 26639 files and directories currently installed.)
Unpacking j2re1.4 (from .../j2re1.4_1.4.2.02-1ubuntu3_i386.deb) ...
dpkg: error processing /var/cache/apt/archives/j2re1.4_1.4.2.02-1ubuntu3_i386.deb (--unpack):
 subprocess pre-installation script returned error exit status 10
Errors were encountered while processing:
 /var/cache/apt/archives/j2re1.4_1.4.2.02-1ubuntu3_i386.deb
E: Sub-process /usr/bin/dpkg returned an error code (1)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The package fails to install because it requires the acceptance of the licence agreement, which it will only allow in an interactive installation.&lt;/p&gt;

&lt;p&gt;The typical way to fix this is to pre-seed the debconf database (using `debconf-set-selections`) with the answers to the questions that the j2re1.4 package requires. However, this doesn’t seem to satisfy j2re1.4, and the package still displays the dialog or fails in non-interactive mode. Why is it java that always gives me these hairy problems?&lt;/p&gt;

&lt;p&gt;Anyway, here is the way to fix it. If you manually append the values &lt;i&gt;directly&lt;/i&gt; to the debconf database file, it will work:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;cp /var/cache/debconf/config.dat /var/cache/debconf/config.dat-old

cat &lt;&lt; E_O_DEBCONF &gt;&gt; /var/cache/debconf/config.dat


Name: j2re1.4/jcepolicy
Template: j2re1.4/jcepolicy
Value:
Owners: j2re1.4
Flags: seen


Name: j2re1.4/license
Template: j2re1.4/license
Value: true
Owners: j2re1.4
Flags: seen


Name: j2re1.4/stopthread
Template: j2re1.4/stopthread
Value: true
Owners: j2re1.4
Flags: seen


E_O_DEBCONF&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;i&gt;(For reference, I found these values by making a copy of the config.dat file, running `dpkg-preconfigure` on the j2re1.4 package, and then running a diff between the updated config.dat file and my copy.)&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;Now you’ll be able to successfully install the package in noninteractive mode. This means, for us, one step closer to fully automating our Xen builds!&lt;/p&gt;</description><link>http://log.openmonkey.com/post/38097969</link><guid>http://log.openmonkey.com/post/38097969</guid><pubDate>Thu, 12 Jun 2008 16:16:00 +1000</pubDate><category>linux</category><category>xen</category></item><item><title>Automatic Saving Of Invalid Resources in Rails While Maintaining a Clean RESTful Interface</title><description>&lt;p style="text-align: center"&gt;or&lt;/p&gt;
&lt;p style="text-align: center"&gt;How To Change Your World In One Line Of Code&lt;/p&gt;

&lt;p&gt;One of the cool things that we’re doing at the AMC is building a large collection of loosely coupled Rails applications that communicate using REST. This is slightly unusual, as rails is predominantly used to build single apps that operate in isolation. In our experiences, we’ve picked up a number of tricks that we’d like to share. Here’s the first, on how a single this single line of code has saved us weeks of time and effort. Here’s the line in question:&lt;/p&gt;

&lt;pre&gt;&lt;code class="ruby"&gt;if @bank_transaction.save &amp;&amp; @bank_transaction.valid?&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Read on to find out how this helps!&lt;/p&gt;

&lt;h3&gt;RESTful Resources&lt;/h3&gt;

&lt;p&gt;This line is part of a new Rails app we’re developing to centrally handle all online payments for our software systems at the AMC. In this payments application, we expose two key resources over REST: (1) line items, which the other apps create with the details of the items to purchase, and (2) bank transactions, which are passed line item IDs and credit card details for the purchase.&lt;/p&gt;

&lt;p&gt;Naturally, the models behind these resources have a bunch of validation rules that ensure certain conditions are met before they can be saved successfully. If any of these requirements are not met, then the model fails to save and the error hash is returned to the client app.&lt;/p&gt;

&lt;p&gt;For most resources, these error hashes are returned in the usual Rails-like way. Let’s look at how LineItemsController does it:&lt;/p&gt;

&lt;pre&gt;&lt;code class="ruby"&gt;class LineItemsController &lt; ApplicationController
  # POST /line_items.xml
  def create
    @line_item = LineItem.new params[:line_item]
    respond_to do |format|
      if @line_item.save
        format.xml  { render :xml =&gt; @line_item, :status =&gt; :created, :location =&gt; @line_item }
      else
        format.xml  { render :xml =&gt; @line_item.errors, :status =&gt; :unprocessable_entity }
      end
    end
  end
end&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To paraphrase: if the items save successfully, return success and the line item in XML, otherwise return the error hash. Nice and predictable, nothing exciting here.&lt;/p&gt;

&lt;h3&gt;Saving Invalid Resources&lt;/h3&gt;

&lt;p&gt;One of the resources in the payments app is different. These are the BankTransactions, which are about as “mission critical” as we get. Let’s talk about the successful case first: during the creation of the BankTransaction model, if all the validations have passed, a before_create callback is triggered that will talk to the bank (using ActiveMerchant, of course) and ask to make the transaction there. If this succeeds, the model is saved to the database and an XML representation of the saved model is passed back to the client application with a success code.&lt;/p&gt;

&lt;p&gt;However, if the transaction with the bank fails, this is still information we care about. A failed transaction could be an indication of a larger problem, and also needs to be recorded for customer service purposes. It makes sense to save every failed transaction as well as every successful one. To this end, the callback that communicates with the bank always returns true, which allows the save continue, and records for both successful and unsuccessful transactions to be kept in the database.&lt;/p&gt;

&lt;p&gt;&lt;i&gt;(Another approach to this problem would be to create a separate “TransactionLog” model to store the transaction data, but this approach requires extra work. Having the BankTransactions save every time is essentially free. Excellent.)&lt;/i&gt;&lt;/p&gt;

&lt;h3&gt;Keeping the REST API Simple&lt;/h3&gt;

&lt;p&gt;While the payments app is saving unsuccessful transactions, the client apps do not want to keep these records around: all they care about is if a transaction is successful or not. The easiest way to make it simple for the clients is to make the creation of a bank transaction resource behave the same way as creating any other resource over REST in rails. This means that if a transaction with the bank fails, then it should *appear* to the clients as if the save also failed.&lt;/p&gt;

&lt;p&gt;This will require the controller to generate an errors hash if the transaction fails. This means that the model should be invalid at this point. Given that we save to the database even for failed transactions, the model should therefore be invalid after the save:&lt;/p&gt;

&lt;pre&gt;&lt;code class="ruby"&gt;class BankTransaction &lt; ActiveRecord::Base  
  validate :must_be_successful_if_saved
  before_create :transact
  
  private
  
  def transact
    # talk to the bank here, and set self.success to true or false pending the results
    # return true to make sure a save always occurs
    true
  end
  
  def must_be_successful_if_saved
    errors.add_to_base("failed to transact successfully with the bank") if !new_record? &amp;&amp; !success?
  end
end&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And then, in the BankTransactionsController, that one magic line:&lt;/p&gt;

&lt;pre&gt;&lt;code class="ruby"&gt;class BankTransactionsController &lt; ApplicationController
  # POST /bank_transactions.xml
  def create
    @bank_transaction = BankTransaction.new params[:bank_transaction]
    respond_to do |format|            
      if @bank_transaction.save &amp;&amp; @bank_transaction.valid?
        format.xml  { render :xml =&gt; @bank_transaction, :status =&gt; :created, :location =&gt; @bank_transaction }
      else
        format.xml  { render :xml =&gt; @bank_transaction.errors, :status =&gt; :unprocessable_entity }
      end
    end
  end
end&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Unlike the standard behaviour shown in the LineItemsController above, we only return a successfully created model if the save is successful AND it is still valid afterwards. A saved model for a failed transaction with the bank will be invalid at this point, so it will return the errors hash. To the client app, this looks like the same resource it tried to create initially, and so it can proceed as usual to display the errors, ask for corrections if necessary, and try to save again. In the background, the payments app has saved every failed transaction for safe keeping.&lt;/p&gt;</description><link>http://log.openmonkey.com/post/37730793</link><guid>http://log.openmonkey.com/post/37730793</guid><pubDate>Tue, 10 Jun 2008 00:04:00 +1000</pubDate><category>rails</category></item><item><title>"FYI: the plumber used the tea towels in the kitchen to mop up the floor, use at your own risk."</title><description>“FYI: the plumber used the tea towels in the kitchen to mop up the floor, use at your own risk.”&lt;br/&gt;&lt;br/&gt; - &lt;em&gt;This is why I bring my own cutlery to work.&lt;/em&gt;</description><link>http://log.openmonkey.com/post/36841385</link><guid>http://log.openmonkey.com/post/36841385</guid><pubDate>Mon, 02 Jun 2008 13:46:32 +1000</pubDate></item><item><title>God init script for Debian/Ubuntu systems</title><description>To compliment the Red Hat &lt;a href="http://pastie.caboo.se/110483"&gt;init script&lt;/a&gt; that is available on the net, here’s the init script we use to launch &lt;a href="http://god.rubyforge.org/"&gt;god&lt;/a&gt; on our Ubuntu systems.

To use, save as /etc/init.d/god, make the file executable, then run `update-rc.d god defaults`. You’re done.

&lt;pre&gt;&lt;code&gt;#!/bin/sh


### BEGIN INIT INFO
# Provides:             god
# Required-Start:       $all
# Required-Stop:        $all
# Default-Start:        2 3 4 5
# Default-Stop:         0 1 6
# Short-Description:    God
### END INIT INFO


NAME=god
DESC=god


set -e


# Make sure the binary and the config file are present before proceeding
test -x /usr/bin/god || exit 0


# Create this file and put in a variable called GOD_CONFIG, pointing to
# your God configuration file
test -f /etc/default/god &amp;&amp; . /etc/default/god
[ $GOD_CONFIG ] || exit 0


. /lib/lsb/init-functions


RETVAL=0


case "$1" in
  start)
    echo -n "Starting $DESC: "
    /usr/bin/god -c $GOD_CONFIG -P /var/run/god.pid -l /var/log/god.log
    RETVAL=$?
    echo "$NAME."
    ;;
  stop)
    echo -n "Stopping $DESC: "
    kill `cat /var/run/god.pid`
    RETVAL=$?
    echo "$NAME."
    ;;
  restart)
    echo -n "Restarting $DESC: "
    kill `cat /var/run/god.pid`
    /usr/bin/god -c $GOD_CONFIG -P /var/run/god.pid -l /var/log/god.log
    RETVAL=$?
    echo "$NAME."
    ;;
  status)
    /usr/bin/god status
    RETVAL=$?
    ;;
  *)
    echo "Usage: god {start|stop|restart|status}"
    exit 1
    ;;
esac


exit $RETVAL&lt;/code&gt;&lt;/pre&gt;</description><link>http://log.openmonkey.com/post/36103083</link><guid>http://log.openmonkey.com/post/36103083</guid><pubDate>Tue, 27 May 2008 01:14:00 +1000</pubDate><category>linux</category><category>ruby</category><category>god</category></item></channel></rss>
