<?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">
  <title>Inventive Labs: Weblog</title>
  
  <id>/weblog</id>
  <updated>
    2009-09-11T00:00:00Z 
  </updated>
  <author>
    <name>inventivelabs.com.au</name>
  </author>
  
    <link rel="self" href="http://feeds.feedburner.com/inventivelabs-weblog" type="application/atom+xml" /><feedburner:browserFriendly></feedburner:browserFriendly><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><entry>
      <title>Get your Notepods!</title>
      <link href="http://inventivelabs.com.au:80/weblog/post/get-your-notepods/" rel="alternate" />
      <id>/weblog/post/get-your-notepods/</id>
      <updated>
        2009-09-11T15:55:10Z
      </updated>
      <author>
        <name>Virginia Murdoch</name>
      </author>
      <content type="html">&lt;p&gt;Inventive Labs has teamed up with &lt;a href="http://jackywinter.com"&gt;the Jacky Winter Group&lt;/a&gt; to produce &lt;a href="http://notepod.net"&gt;Notepods&lt;/a&gt; - sketchbooks for your app ideas, in a package reminiscent of your favourite smartphone.&lt;/p&gt;

&lt;p&gt;&lt;img alt="Notepod3" src="http://inventivelabs.com.au:80/static/files/assets/f06ade00/notepod3_blogwidth.jpg" title="Notepod3" /&gt;&lt;/p&gt;

&lt;p&gt;It's incredibly fun to come up with an idea in the pub over a few beers and have it produced in the space of ten short days, website and all. &lt;a href="http://notepod.net"&gt;Get yours now!&lt;/a&gt;.&lt;/p&gt;
</content>
    </entry>
  
    <entry>
      <title>Harry, we are here to help*</title>
      <link href="http://inventivelabs.com.au:80/weblog/post/harry-we-are-here-to-help/" rel="alternate" />
      <id>/weblog/post/harry-we-are-here-to-help/</id>
      <updated>
        2009-02-06T12:17:13Z
      </updated>
      <author>
        <name>Virginia Murdoch</name>
      </author>
      <content type="html">&lt;p&gt;Last week, our colleagues at &lt;a href="http://www.collierdesign.com.au"&gt;Collier and Associates&lt;/a&gt; asked if we'd donate some of our time to building a website in support of Harry Nicolaides, the Australian writer who has been imprisoned in Thailand for defaming the King. We, of course, were happy to oblige: freedom of expression is something we all hold dear.&lt;/p&gt;

&lt;p&gt;Collier contributed the design and we put the site together in Blueprint; the whole process took about a day (in searing temperatures during last week's heatwave), and the site is now online.&lt;/p&gt;

&lt;p&gt;You can help &lt;a href="http://bringharryhome.com/"&gt;bring Harry home&lt;/a&gt; by signing the petition or donating money.&lt;/p&gt;

&lt;p&gt;&lt;small&gt;* A reference to a &lt;a href="http://www.imdb.com/title/tt0216800/"&gt;French thriller&lt;/a&gt; whose title I, for some reason, have never been able to forget.&lt;/small&gt;&lt;/p&gt;
</content>
    </entry>
  
    <entry>
      <title>Peeking at previews in Blueprint</title>
      <link href="http://inventivelabs.com.au:80/weblog/post/peeking-at-previews-in-blueprint/" rel="alternate" />
      <id>/weblog/post/peeking-at-previews-in-blueprint/</id>
      <updated>
        2008-12-26T17:25:49Z
      </updated>
      <author>
        <name>Joseph</name>
      </author>
      <content type="html">&lt;p&gt;I just uploaded a little Quicktime movie of a recent change to Blueprint: &lt;a href="http://pockets.josephpearson.org/abc7/bp_sprung_preview.mov"&gt;spring-loaded previews&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It's only a small change, but one that has been on my list since, oh, the 4th of December 2006 — when our version control system tells me I started working on the 'drafting' functionality that is at the core of Blueprint.&lt;/p&gt;

&lt;p&gt;Formerly, you just clicked the &lt;em&gt;Preview&lt;/em&gt; tab to check your changes before saving them. The preview would pop up in the same grey frame you see in the movie. The recent change was to animate the transition — and to allow you to "pull" on the tab to "peek" at the preview.&lt;/p&gt;

&lt;p&gt;Like I said, it's a small change, and you could argue it's mere eye candy. But it has stayed on my list of things to do because exactly these sorts of user interactions give an interface its &lt;em&gt;handling&lt;/em&gt; — the sense that you're steering it, rather than poking at it. From this, the people using the system develop a confidence and an instinct.&lt;/p&gt;
</content>
    </entry>
  
    <entry>
      <title>Migrating from Lighthouse to Redmine</title>
      <link href="http://inventivelabs.com.au:80/weblog/post/migrating-from-lighthouse-to-redmine/" rel="alternate" />
      <id>/weblog/post/migrating-from-lighthouse-to-redmine/</id>
      <updated>
        2008-12-15T17:46:27Z
      </updated>
      <author>
        <name>Joseph</name>
      </author>
      <content type="html">&lt;p&gt;We love &lt;a href="http://lighthouseapp.com"&gt;Lighthouse&lt;/a&gt;, but here at the Labs our needs have begun to change and Lighthouse no longer feels like a best fit for us. So we've upped stumps and moved to &lt;a href="http://redmine.org"&gt;Redmine&lt;/a&gt;, which is less flashy but better complements our workflow (such as it is).&lt;/p&gt;

&lt;p&gt;I took a couple of hours to write a rake task that politely exploits the Lighthouse API and imports almost all available data from that system into a Redmine instance.&lt;/p&gt;

&lt;p&gt;Here it is:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://inventivelabs.com.au:80/static/files/assets/32bfd614/lighthouse_import.rake" class="download blank"&gt;&lt;img alt="Lighthouse import.rake" src="http://inventivelabs.com.au:80/images/mimetypes/blank.gif?1229052343" title="Lighthouse import.rake" /&gt;&lt;label&gt;&lt;span&gt;Download file (5Kb)&lt;/span&gt;&lt;br /&gt;&lt;em&gt;lighthouse_import.rake&lt;/em&gt;&lt;/label&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;Steps:&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Install the task file into &lt;code&gt;lib/tasks&lt;/code&gt; in your Redmine directory.&lt;/li&gt;
&lt;li&gt;Add the &lt;a href="http://github.com/Caged/lighthouse-api/tree/master"&gt;lighthouse-api ruby library&lt;/a&gt; as a plugin. This essentially involves running the following command from Redmine's &lt;em&gt;&lt;code&gt;vendor/plugins&lt;/code&gt;&lt;/em&gt; directory:
&lt;pre&gt;&lt;code&gt;$ git clone git://github.com/Caged/lighthouse-api.git .&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;If you haven't already:

&lt;ul&gt;
&lt;li&gt;create your database and configure your &lt;code&gt;database.yml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rake db:migrate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rake redmine:load_default_data&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Log into Redmine and create the projects that will contain the data you're importing from Lighthouse. Configure them however you like — in order for the import to happen, they must be named &lt;em&gt;exactly&lt;/em&gt; as they are in Lighthouse.&lt;/li&gt;
&lt;li&gt;You can similarly create users and assign them to these projects. Any users with matching names to Lighthouse users will be recognised by the importer.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;In Lighthouse, set up an API token if you haven't already. This can (and should) be a read-only token.&lt;/li&gt;
&lt;li&gt;Now run the script:
&lt;pre&gt;&lt;code&gt;$ rake import:lighthouse LH_ACCOUNT=abc LH_TOKEN=xyz...321&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Obviously the &lt;code&gt;LH_ACCOUNT&lt;/code&gt; and &lt;code&gt;LH_TOKEN&lt;/code&gt; environment variables should be entered as per your Lighthouse account. You can also pass in &lt;code&gt;CREATE_USERS=1&lt;/code&gt; to actually create user accounts where they are referenced in the Lighthouse data — they will have dummy names and email addresses, and their passwords will match their login names. If you don't pass in this flag, any unrecognised user references will be assigned to the first admin user.&lt;/p&gt;

&lt;p&gt;The script could use a little refactoring, but it worked very well for our needs. Let me know how you go.&lt;/p&gt;
</content>
    </entry>
  
    <entry>
      <title>A Bloggie for Ms Fits</title>
      <link href="http://inventivelabs.com.au:80/weblog/post/a-bloggie-for-ms-fits/" rel="alternate" />
      <id>/weblog/post/a-bloggie-for-ms-fits/</id>
      <updated>
        2008-03-18T15:43:13Z
      </updated>
      <author>
        <name>Virginia Murdoch</name>
      </author>
      <content type="html">&lt;p&gt;The fabulous &lt;a href="http://reasonsyouwillhateme.com"&gt;Reasons You Will Hate Me&lt;/a&gt; has just been &lt;a href="http://2008.bloggies.com"&gt;honoured with a Bloggie&lt;/a&gt; for Best Australian Weblog. Inventive Labs designed Ms Fits an appropriately sexy new site in the middle of 2007, and built a custom content management system to suit some of her specific requirements - in particular, the now world-famous 'Friday Q&amp;amp;A'.&lt;/p&gt;

&lt;p&gt;&lt;img alt="Rywhm" src="http://inventivelabs.com.au:80/static/files/assets/103861/rywhm.png" title="Rywhm" /&gt; &lt;/p&gt;

&lt;p&gt;Congratulations, Ms Fits!&lt;/p&gt;</content>
    </entry>
  
    <entry>
      <title>The other things</title>
      <link href="http://inventivelabs.com.au:80/weblog/post/the-other-things/" rel="alternate" />
      <id>/weblog/post/the-other-things/</id>
      <updated>
        2008-02-25T10:48:33Z
      </updated>
      <author>
        <name>Joseph</name>
      </author>
      <content type="html">&lt;p&gt;We do have a habit of saying how busy we are at the Labs, to the extent that it's almost a little obnoxious. We do, in fact, occasionally find time to craft software that is not for particular paying clients, but has a wider audience.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://inventivelabs.com.au/weblog/post/the-writing-s-on-the-magnetic-wall"&gt;Fridge&lt;/a&gt;, our Facebook app, is an example of this. That was a fun project, and the reception has been really rewarding. I worry about the commercial trajectory and privacy implications of Facebook, but in developing Fridge I gained a high regard for their technical platform.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://gadgets.inventivelabs.com.au/tabulate" title="Demo graphic" &gt;&lt;img alt="Demo graphic" class="inline" src="http://inventivelabs.com.au:80/static/files/assets/102594/demo_graphic_blogwidth.png" title="Demo graphic" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;By far our most popular project is &lt;a href="http://gadgets.inventivelabs.com.au/tabulate"&gt;Tabulate&lt;/a&gt;, released in October last year, for iPhones and iPod Touches. It gives you a slick interface for, well, power-browsing in multiple windows with MobileSafari. Tens of thousands of Apple device owners are using it.&lt;/p&gt;

&lt;p&gt;Also for iPhones, released by stealth and only shortly after Tabulate, is &lt;a href="http://code.google.com/p/kapow"&gt;Kapow!&lt;/a&gt; -- a digital comic book viewer. I think only about three people in the world use it, but they seem to love it.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://gadgets.inventivelabs.com.au/castanaut" title="Gadget" &gt;&lt;img alt="Gadget" class="neg_column" src="http://inventivelabs.com.au:80/static/files/assets/102597/gadget_admin_icon.png" title="Gadget" /&gt;&lt;/a&gt; On the weekend we released another project that's been in (mostly weekend) development for a month or so: &lt;a href="http://gadgets.inventivelabs.com.au/castanaut"&gt;Castanaut&lt;/a&gt;. In technical terms this is a Ruby DSL for writing executable scripts that control input and application interaction during screencast recording. &lt;/p&gt;

&lt;p&gt;In layman's terms it's a way you can take the screencast notes that you're already writing down on paper before recording, &lt;strong&gt;double-click&lt;/strong&gt; them, and have a screencast saved and ready for publishing. It's kinda freaky.&lt;/p&gt;

&lt;h3&gt;The many tentacles of Inventive Labs&lt;/h3&gt;

&lt;p&gt;So, we've had a recent proliferation of subdomains here at the Labs. Two of them may interest you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://gadgets.inventivelabs.com.au"&gt;Gadgets&lt;/a&gt;: this is where we put the publicly available goodies we develop.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://wisdom.inventivelabs.com.au"&gt;Wisdom&lt;/a&gt;: a more frequently updated stream of mostly technical but occasionally humourous objects that preoccupy us.&lt;/li&gt;
&lt;/ul&gt;</content>
    </entry>
  
    <entry>
      <title>Eight days at the Labs</title>
      <link href="http://inventivelabs.com.au:80/weblog/post/eight-days-at-the-labs/" rel="alternate" />
      <id>/weblog/post/eight-days-at-the-labs/</id>
      <updated>
        2007-11-27T12:31:21Z
      </updated>
      <author>
        <name>Joseph</name>
      </author>
      <content type="html">&lt;p&gt;One of the items on our extremely well-populated todo list is to update our &lt;a href="http://inventivelabs.com.au:80/portfolio"&gt;Portfolio&lt;/a&gt; page. It'll happen  shortly, but meantime I thought I'd give you a quick look at three sites we've launched in the last eight days.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.readings.com.au"&gt;&lt;img alt="readings" src="http://inventivelabs.com.au:80/static/files/assets/101230/readings_blogwidth.jpg" title="readings" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Several months in the making, the new &lt;a href="http://www.readings.com.au"&gt;Readings&lt;/a&gt; website gives this iconic Melbourne business the online stature it deserves. Integrating with their inventory and ordering systems, the site makes fascinating use of "product collections" to recreate the serendipity of browsing in-store. Added to that is a powerful and greatly simplified search, regular postings of events, news, product reviews and downloads, and a shopping basket that is secure, intuitive and remarkably fun.&lt;/p&gt;

&lt;p&gt;We're rather proud of the Readings website, which we built from scratch in &lt;a href="http://www.rubyonrails.org"&gt;Rails&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://bestqualitycrab.com"&gt;BestQualityCrab&lt;/a&gt; is an unusual experiment in the convergence of blogging and geography. &lt;a href="http://bestqualitycrab.com"&gt;&lt;img alt="bqc" class="neg_column" src="http://inventivelabs.com.au:80/static/files/assets/101234/bqc.jpg" title="bqc" /&gt;&lt;/a&gt; We constructed the site in Blueprint for a small cohort of Melbourne academics, who explain it like this: &lt;/p&gt;

&lt;p&gt;&lt;em&gt;It starts from the belief that blogs are usually created around time-centred ways of organising thoughts and narratives. BestQualityCrab starts from the idea that blog thinking and narratives can be organised in terms of space and place as well as time.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Blogs are rarely quite this interesting to explore. In fact, we think BQC might herald an entirely new category of weblogs.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.wired-consulting.com.au"&gt;
&lt;img alt="wired" class="neg_column" src="http://inventivelabs.com.au:80/static/files/assets/101238/wired.jpg" title="wired" /&gt;&lt;/a&gt; &lt;a href="http://www.wired-consulting.com.au"&gt;Wired Consulting&lt;/a&gt; is a new Australian telecommunications consultancy with a remarkable pool of talent. This is our second collaboration with David Koopmans of &lt;a href="http://www.mokummarketing.com"&gt;Mokum Marketing&lt;/a&gt;, and once again shows the flexibility of Blueprint.&lt;/p&gt;

&lt;p&gt;&lt;br style="clear: both"&gt;&lt;/p&gt;

&lt;p&gt;Three big sites in a little over a week is not bad for a team of two inventors. Obviously our pace isn't always so quick, but since August we've launched 14 Blueprint sites of varying complexity. And you wonder why we love our little CMS so much.&lt;/p&gt;</content>
    </entry>
  
    <entry>
      <title>Scheduling periodic tasks in a Rails application</title>
      <link href="http://inventivelabs.com.au:80/weblog/post/scheduling-periodic-tasks-in-a-rails-application/" rel="alternate" />
      <id>/weblog/post/scheduling-periodic-tasks-in-a-rails-application/</id>
      <updated>
        2007-11-08T17:49:28Z
      </updated>
      <author>
        <name>Joseph</name>
      </author>
      <content type="html">&lt;p&gt;There's a number of solutions around for forking out of the Rails http request cycle and performing intermittent or long-running operations within your Rails application environment. Some of the most well-documented options are summarised on the &lt;a href="http://wiki.rubyonrails.org/rails/pages/HowToRunBackgroundJobsInRails"&gt;Rails wiki&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Looking for a way to schedule actions like 'fetch this feed every 15 minutes', 'clear out old sessions from the db', and 'take a snapshot of each site' for Blueprint, I investigated most of these options.&lt;/p&gt;

&lt;p&gt;In the past I've used the reliable cron/runner combination, but this requires additional setup -- outside of your app -- on each server where the app is deployed. There are recipes out there for updating the cron table via Capistrano, but this is difficult to do in a way that preserves existing cron configuration. BackgrounDrb and AP4R both look accomplished, but are really intended to solve other (and bigger) problems.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://openwferu.rubyforge.org/"&gt;OpenWFEru&lt;/a&gt; is a Workflow Engine, which sounds impressively enterprisey. I don't know much about that, but it comes with a pretty good scheduler &amp;#8212; check out the API for it &lt;a href="http://openwferu.rubyforge.org/rdoc/classes/OpenWFE/Scheduler.html"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This can be used to create a cron-like scheduler within Rails. I wanted the scheduler to run as a separate process alongside the pack of Mongrels that handle web requests, started and stopped by Capistrano. Ruby has a great 'daemons' gem that takes care of daemonising processes for you.&lt;/p&gt;

&lt;p&gt;If you need something like this, install these two gems:&lt;/p&gt;

&lt;pre&gt;
$ sudo gem install -y openwferu daemons
&lt;/pre&gt;

&lt;p&gt;Then create script/schedule:&lt;/p&gt;

&lt;pre&gt;
#!/usr/bin/env ruby
require 'rubygems'
require 'daemons'
APP_DIR = File.join(File.dirname(File.expand_path(__FILE__)), '..')

Daemons.run_proc(
  'schedule',
  :dir_mode =&gt; :normal, 
  :dir =&gt; File.join(APP_DIR, 'log'),
  :multiple =&gt; false,
  :backtrace =&gt; true,
  :monitor =&gt; true,
  :log_output =&gt; true
) do

  # Daemonising changes the pwd to /, so we need to switch 
  # back to RAILS_ROOT.
  Dir.chdir(APP_DIR)

  # Load our Rails environment.
  require File.join('config', 'environment')

  begin
    # Initialise the OpenWFE scheduler object.
    require 'openwfe/util/scheduler'
    scheduler = OpenWFE::Scheduler.new
    scheduler.start

    # Now assign jobs to the scheduler (see API). For example:
    scheduler.schedule_every('15m') { Feed.fetch_and_consume! }

    # Tell the scheduler to perform these jobs until the 
    # process is stopped.
    scheduler.join
  rescue =&gt; e
    RAILS_DEFAULT_LOGGER.warn "Exception in schedule: #{e.inspect}"
    exit
  end
end
&lt;/pre&gt;

&lt;p&gt;Chmod this file to be executable, then test it:&lt;/p&gt;

&lt;pre&gt;
$ chmod a+x script/schedule
$ script/schedule start
&lt;/pre&gt;

&lt;p&gt;Monitor the file at log/schedule.output -- most errors will be reported here. It will log to log/&amp;lt;environment&amp;gt;.log. Run 'script/schedule' with no arguments to see how to stop and restart it, and do other daemonoid things if you like.&lt;/p&gt;

&lt;p&gt;You can then create a task within your Capistrano deployment recipies along the lines of:&lt;/p&gt;

&lt;pre&gt;
task :after_restart do
  run "RAILS_ENV=#{rails_env} #{current_path}/script/schedule stop"
  run "RAILS_ENV=#{rails_env} #{current_path}/script/schedule start"
end
&lt;/pre&gt;</content>
    </entry>
  
    <entry>
      <title>Using Blueprint as a Drafting Tool</title>
      <link href="http://inventivelabs.com.au:80/weblog/post/using-blueprint-as-a-drafting-tool/" rel="alternate" />
      <id>/weblog/post/using-blueprint-as-a-drafting-tool/</id>
      <updated>
        2007-09-11T10:50:03Z
      </updated>
      <author>
        <name>Virginia Murdoch</name>
      </author>
      <content type="html">&lt;p&gt;A couple of weeks ago I spent a day designing and building simple default templates for each of &lt;a href="http://inventivelabs.com.au:80/blueprint"&gt;Blueprint&lt;/a&gt;'s core page types ('blueprints'), ostensibly to help us demonstrate the system more easily to clients and friends. There are currently ten generic blueprints – Page, News, Links, Search, Events, Contact, Projects, Blog, Gallery and People – and each of them has one or more templates, all mixing a bit of logic with some HTML. There are companion javascript and CSS files, too.&lt;/p&gt;

&lt;p&gt;&lt;img alt="blueprint_default" class="inline" src="http://inventivelabs.com.au:80/static/files/assets/6650/blueprint_default.png" /&gt; &lt;/p&gt;

&lt;p&gt;A by-product of completing this task is that Blueprint has gone from being something I started working with after the architecture of a new site was established (most often with pen and paper, followed by laborious and under-appreciated site maps put together in &lt;s&gt;Illustrator&lt;/s&gt; &lt;a href="http://www.freeverse.com/apps/app/?id=6020"&gt;Lineform&lt;/a&gt;), to being an indispensible tool in the drafting process: I can create a working site prototype in mere seconds, and manipulate it quickly within the drag-n-drop interface, testing hypotheses and seeing roadblocks much sooner than if I were starting from sketches.&lt;/p&gt;

&lt;p&gt;&lt;img alt="structure" class="inline" src="http://inventivelabs.com.au:80/static/files/assets/6654/structure.png" /&gt; &lt;/p&gt;

&lt;p&gt;Obviously this isn't a substitute for thinking through the more complex IA requirements of larger sites, but for a typical small Blueprint site it's both appropriate and efficient, since whatever is achieved at this stage is carried over seamlessly into the development. &lt;/p&gt;

&lt;p&gt;It's also considerably easier for people we're working with to understand, since the results have the dimension and interactivity of a finished site.&lt;/p&gt;

&lt;p&gt;&lt;img alt="preview" class="inline" src="http://inventivelabs.com.au:80/static/files/assets/6659/preview.png" /&gt; &lt;/p&gt;</content>
    </entry>
  
    <entry>
      <title>Like that but with this: view inheritance in Rails</title>
      <link href="http://inventivelabs.com.au:80/weblog/post/like-that-but-with-this-view-inheritance-in-rails/" rel="alternate" />
      <id>/weblog/post/like-that-but-with-this-view-inheritance-in-rails/</id>
      <updated>
        2007-09-05T16:47:16Z
      </updated>
      <author>
        <name>God</name>
      </author>
      <content type="html">&lt;p&gt;Yesterday I wrote about &lt;a href="http://www.inventivelabs.com.au/weblog/post/providing-default-content-in-rails-layouts"&gt;a simple method&lt;/a&gt; that lets you to place default content in your Rails layouts. This default content can be optionally overridden by your views using the standard-issue &lt;code&gt;content_for&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It's handy enough on its own, but it's also a piece of a somewhat larger puzzle — something I've called view inheritance. The principle is that, just as Ruby classes can extend from parent classes, so ERB templates should be able to extend from parent templates.&lt;/p&gt;

&lt;p&gt;Consider this template, a.html.erb:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;html&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;% default_content_for :title do %&amp;gt;
  &amp;lt;h1&amp;gt;Bonjour&amp;lt;/h1&amp;gt;
&amp;lt;% end %&amp;gt;
&amp;lt;/html&amp;gt;
&amp;lt;/body&amp;gt;
&lt;/pre&gt;

&lt;p&gt;Now b.html.erb, which &lt;em&gt;inherits&lt;/em&gt; a.html.erb:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;% inheriting_view 'a' do %&amp;gt;
  &amp;lt;% content_for :title do %&amp;gt;
    &amp;lt;h1&amp;gt;Aloha&amp;lt;/h1&amp;gt;
  &amp;lt;% end %&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/pre&gt;

&lt;p&gt;That says "do everything that the first template does, but make the title say 'Aloha'".&lt;/p&gt;

&lt;p&gt;Of course we're not doing much different from what a layout does yet — we've just shifted responsibility for specifying the layout file from the controller (or outer view) to the view itself. Sometimes that's not very useful, and although view inheritance can be wielded as a general replacement for layouts, I'm not really recommending it.&lt;/p&gt;

&lt;p&gt;But what about this scenario? You have a model X, and an XController that serves up a form for X. Then you need a model Y, which is like X but with a few extra fields. So you create the Y model:&lt;/p&gt;

&lt;pre&gt;
class Y &amp;lt; X
&lt;/pre&gt;

&lt;p&gt;And perhaps a YController:&lt;/p&gt;

&lt;pre&gt;
class YController &amp;lt; XController
&lt;/pre&gt;

&lt;p&gt;But how do you add the extra fields to the form? Like this:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;% inheriting_view 'x/form' do %&amp;gt;
  &amp;lt;% content_for :additional_fields do %&amp;gt;
    &amp;lt;fieldset&amp;gt;
      &amp;lt;legend&amp;gt;Geography&amp;lt;/legend&amp;gt;
      &amp;lt;label&amp;gt;Latitude
        &amp;lt;%= @f.text_field('latitude') %&amp;gt;
      &amp;lt;/label&amp;gt;
      &amp;lt;label&amp;gt;Longitude
        &amp;lt;%= @f.text_field('longitude') %&amp;gt;
      &amp;lt;/label&amp;gt;
    &amp;lt;/fieldset&amp;gt;
  &amp;lt;% end %&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/pre&gt;

&lt;p&gt;So &lt;acronym title="Don't Repeat Yourself"&gt;DRY&lt;/acronym&gt; you'll need a beer when you're done.&lt;/p&gt;

&lt;p&gt;Here's the code:&lt;/p&gt;

&lt;pre&gt;
  def inheriting_view(options = {}, &amp;block)
    # We accept a shorthand syntax -- if options is a string, render as a file.
    return inheriting_view({:file =&gt; options}, &amp;block) if options.is_a?(String)

    bind = options[:binding]

    # Get our differences and additions to the view we're inheriting.
    if block_given?
      bind ||= block.binding
      instance_variable_set(:@content_for_layout, capture(&amp;block)) 
    end

    raise "Important: inheriting_view() requires a block. " +
      "An empty block (eg, using {}) is suitable." unless bind 

    # If we're inheriting a partial, lend our local context to that partial.
    options[:locals] = eval("local_assigns", bind) if options[:partial]

    # Render our parent view.
    concat(render(options), bind)
  end
&lt;/pre&gt;

&lt;p&gt;(Yes, this works for partials inheriting other partials too.)&lt;/p&gt;</content>
    </entry>
  
    <entry>
      <title>Providing default content in Rails layouts</title>
      <link href="http://inventivelabs.com.au:80/weblog/post/providing-default-content-in-rails-layouts/" rel="alternate" />
      <id>/weblog/post/providing-default-content-in-rails-layouts/</id>
      <updated>
        2007-09-04T11:11:10Z
      </updated>
      <author>
        <name>Joseph</name>
      </author>
      <content type="html">&lt;p&gt;Another quick tip for Rails view developers. We commonly employ the following snippet in our layouts:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;%= (out=yield :sidebar) ? out : render(:partial =&gt; 'sidebar') %&amp;gt;
&lt;/pre&gt;

&lt;p&gt;This thorny little one-liner says, if the view we're rendering for this layout has &lt;code&gt;&lt;a href="http://api.rubyonrails.org/classes/ActionView/Helpers/CaptureHelper.html#M000638"&gt;content_for&lt;/a&gt; :sidebar&lt;/code&gt;, show that content. Otherwise show the default content (which we've tucked away in the 'sidebar' partial).&lt;/p&gt;

&lt;p&gt;It's a handy pattern because it lets your views &lt;em&gt;opt&lt;/em&gt; to modify some aspect of the layout, without having to — they can go with the default sidebar content if they don't care.&lt;/p&gt;

&lt;p&gt;However. Firstly, it's a bit unsightly. Secondly, you must put the default content in a partial (unless it's short enough to just be a string — an approach that is arguably uglier). In most cases, the default content for a block in a layout is best described in that layout, so that you don't have to go hunting down other files to get a picture of what the layout displays. Here's a better way:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;% default_content_for :sidebar do %&amp;gt;
  &amp;lt;p&amp;gt;This is the default sidebar content! (et cetera)&amp;lt;/p&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/pre&gt;

&lt;p&gt;So now, if the view specifies some &lt;code&gt;content_for :sidebar&lt;/code&gt;, the contents of the block above (ie, the &amp;lt;p&amp;gt; tag) will &lt;em&gt;not&lt;/em&gt; be rendered. If the view doesn't do that, then the layout will render the contents of the block.&lt;/p&gt;

&lt;p&gt;If you'd prefer this idiom in your views, drop the following method into application_helper.rb:&lt;/p&gt;

&lt;pre&gt;
def default_content_for(name, &amp;block)
  name = name.kind_of?(Symbol) ? ":#{name}" : name
  out = eval("yield #{name}", block.binding)
  concat(out || capture(&amp;block), block.binding)
end
&lt;/pre&gt;

&lt;p&gt;This method is full of &lt;a href="http://corelib.rubyonrails.org/classes/Binding.html"&gt;binding&lt;/a&gt;s and &lt;a href="http://api.rubyonrails.org/classes/ActionView/Helpers/CaptureHelper.html#M000637"&gt;capture&lt;/a&gt;s, which you can read up on at your leisure, or feel free to ask questions in the comments.&lt;/p&gt;</content>
    </entry>
  
    <entry>
      <title>The writing's on the magnetic wall</title>
      <link href="http://inventivelabs.com.au:80/weblog/post/the-writing-s-on-the-magnetic-wall/" rel="alternate" />
      <id>/weblog/post/the-writing-s-on-the-magnetic-wall/</id>
      <updated>
        2007-09-03T14:47:58Z
      </updated>
      <author>
        <name>Joseph</name>
      </author>
      <content type="html">&lt;p&gt;Many, many moons ago (thirty-seven, to be exact), I made a little DHTML application called &lt;em&gt;The Fridge&lt;/em&gt;. You had a bunch of word tiles that you pushed and shoved into the shape of a poem, much like &lt;a href="http://www.magneticpoetry.com/"&gt;Magnetic Poetry&lt;/a&gt; on fridges all over the world. It was good fun and much loved by many people, but with the passage of time the flocking crowds  dwindled down to a core of devotees. And then, as is the way of things, they too found other pastimes.&lt;/p&gt;

&lt;p&gt;Last week we took a couple of days out at the Labs to turn The Fridge into a &lt;a href="http://www.facebook.com/apps/application.php?id=10761020563"&gt;Facebook application&lt;/a&gt;. This manifestation is even more fun than the last, because you can now create your own sets of words  and share them with others. Also, crafting miniature epics on your friends' walls for posterity is a rather satisfying pursuit.&lt;/p&gt;

&lt;p&gt;Here's what it looks like:&lt;/p&gt;

&lt;p&gt;&lt;img alt="fridge-wide" src="http://inventivelabs.com.au:80/static/files/assets/6538/fridge_wide_teaser_blogwidth.gif" /&gt; &lt;/p&gt;

&lt;p&gt;If you have a Facebook account, &lt;a href="http://www.facebook.com/apps/application.php?id=10761020563"&gt;take it for a spin&lt;/a&gt;.&lt;/p&gt;</content>
    </entry>
  
    <entry>
      <title>Apache's 'internal dummy connection'</title>
      <link href="http://inventivelabs.com.au:80/weblog/post/apache-s-internal-dummy-connection/" rel="alternate" />
      <id>/weblog/post/apache-s-internal-dummy-connection/</id>
      <updated>
        2007-08-21T00:32:01Z
      </updated>
      <author>
        <name>Joseph</name>
      </author>
      <content type="html">&lt;p&gt;Since version 2.2, the ubiquitous Apache webserver has begun intermittently issuing GET requests to itself. They hit the root '/' url. They appear in your access logs looking something like this:&lt;/p&gt;

&lt;pre style="margin-bottom: 0.8em"&gt;
::1 - - [...] "GET / HTTP/1.0" 403 16 "-" 
"Apache/2.2.4 (Unix) mod_ssl/2.2.4OpenSSL/0.9.8a 
DAV/2 (internal dummy connection)"
&lt;/pre&gt;

&lt;p&gt;And in your Rails logs like this:&lt;/p&gt;

&lt;pre style="margin-bottom: 0.8em"&gt;
Processing Controller#index (for &lt;em&gt;::1&lt;/em&gt; at ...) [GET]
&amp;lt;snip&amp;gt;
200 OK [&lt;em&gt;http://127.0.0.1/&lt;/em&gt;]
&lt;/pre&gt;

&lt;p&gt;Fine, GETs should be &lt;a href="http://en.wikipedia.org/wiki/Idempotent"&gt;idempotent&lt;/a&gt;, so it's completely safe practice and don't you worry, son.  Well, except...&lt;/p&gt;

&lt;p&gt;Here's a common web application pattern wherein GETs are not quite safe: caching on demand. When a resource is requested, the web server is instructed to check whether a cached version of the resource exists (eg, a static .html page). If it does, it is served up. Otherwise, a web application is invoked and asked to serve the page. The web application does so, and saves a cached version for future requests. (It will have some internal or external logic that determines the expiry of cached resources.)&lt;/p&gt;

&lt;p&gt;So there's a scenario where a request might be issued and the result saved for all future requests. What if that request comes from a local, rather than a remote, caller? This can have a bearing on the response. In the case of Ruby on Rails, urls constructed with url_for() use the local domain (127.0.0.1) rather than the remote domain (example.com).&lt;/p&gt;

&lt;p&gt;If the response to an internal request is cached, it's going to get served up to all your remote clients. And all those localhost links aren't going to work for them. This is the problem we have with Apache's 'internal dummy connections': they intermittently corrupt our cache.&lt;/p&gt;

&lt;p&gt;I deal with it rather bluntly:&lt;/p&gt;

&lt;pre style="margin-bottom: 0.8em"&gt;
  RewriteCond %{HTTP_USER_AGENT} ^.*internal\ dummy\ connection.*$ [NC]
  RewriteRule .* - [F,L]
&lt;/pre&gt;

&lt;p&gt;This mod_rewrite snippet, which belongs in your .htaccess file or &lt;code&gt;&amp;lt;VirtualHost&amp;gt;&lt;/code&gt; config, sends the internal connections a 403, keeping them well away from your application.&lt;/p&gt;</content>
    </entry>
  
    <entry>
      <title>5-minute redesign: Ebay</title>
      <link href="http://inventivelabs.com.au:80/weblog/post/5-minute-redesign-ebay/" rel="alternate" />
      <id>/weblog/post/5-minute-redesign-ebay/</id>
      <updated>
        2007-08-08T17:32:23Z
      </updated>
      <author>
        <name>Virginia Murdoch</name>
      </author>
      <content type="html">&lt;p&gt;&lt;br /&gt;
Here's what &lt;a href="http://ebay.com.au"&gt;Ebay&lt;/a&gt; looks like: &lt;/p&gt;

&lt;p&gt;&lt;img alt="ebay_current" src="http://inventivelabs.com.au:80/static/files/assets/6158/ebay_current.gif" /&gt; &lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;I allocated some of my highly valuable time this afternoon to fix it:&lt;/p&gt;

&lt;p&gt;&lt;img alt="ebay_mine" src="http://inventivelabs.com.au:80/static/files/assets/6160/ebay_mine.gif" /&gt; &lt;/p&gt;

&lt;p&gt;It would have been shorter &lt;a href="http://en.wikipedia.org/wiki/Blaise_Pascal"&gt;if I'd had the time&lt;/a&gt;.&lt;/p&gt;</content>
    </entry>
  
    <entry>
      <title>Sortable Tables for Prototype</title>
      <link href="http://inventivelabs.com.au:80/weblog/post/sortable-tables-for-prototype/" rel="alternate" />
      <id>/weblog/post/sortable-tables-for-prototype/</id>
      <updated>
        2007-07-19T18:04:48Z
      </updated>
      <author>
        <name>Joseph</name>
      </author>
      <content type="html">&lt;p&gt;We've used Stuart Langridge's original &lt;a href="http://kryogenix.org/code/browser/sorttable/"&gt;JavaScript library for sorting tables&lt;/a&gt; on a number of projects. As perhaps the first important application of unobtrusive JavaScript techniques, it was a ground-breaking piece of code. But four or five years on, its ~480 lines of code were beginning to look a little hoary. Stuart has recently updated it with a more modern approach, but I still baulk at 20kb of JS for what is often a non-essential UI feature.&lt;/p&gt;

&lt;p&gt;Since we use &lt;a href="http://prototypejs.org"&gt;Prototype&lt;/a&gt; on most projects, my ideal was a slimmed-down implementation of sortable tables that leveraged Prototype to gain some efficiency and concision. Out of long habit I began with a Google search to see if anything had already been done — after all, we're inventors, not re-inventors. I found Inigo Surguy's &lt;a href="http://surguy.net/rediscovering-javascript.html"&gt;impressively brief implementation&lt;/a&gt;, developed during a pedagogical analysis of recent developments in JavaScript.&lt;/p&gt;

&lt;p&gt;Testing it out, I had a few issues with it — it was quite slow, it required markup that I considered unnecessarily verbose, it relied on a superfluous logging library, and there was some potential for namespace clashes when let loose amongst other JS (&lt;code&gt;init()&lt;/code&gt; is a pretty common function name). There was also a function or two that duplicated Prototype functionality. And, as a purely personal aesthetic preference, I like my JavaScript to be OOP and mindful of 80 character line limits.&lt;/p&gt;

&lt;p&gt;So I ended up borrowing Inigo's useful approach to the problem and rewriting the code mostly from scratch. This implementation resolves the issues listed above and does some caching of comparison data to gain a little speed. Also, it's 4kb. Download it:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://inventivelabs.com.au:80/static/files/assets/6157/sorttable.js" 
            class="download blank"&gt;&lt;img alt="Sortable Tables for Prototype" src="http://inventivelabs.com.au:80/images/mimetypes/blank.gif?1187662778" /&gt;&lt;label&gt;&lt;em&gt;Sortable Tables for Prototype&lt;/em&gt; (0.00Mb)&lt;/label&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Like Inigo's version, this is released under the &lt;a href="http://creativecommons.org/licenses/by-sa/2.5/"&gt;Creative Commons Attribution-ShareAlike 2.5 license&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here's a simple demo. Click the headings to re-sort the table:&lt;/p&gt;

&lt;table class="sortable"&gt;
  &lt;tr&gt;
    &lt;th sort="integer"&gt;ID&lt;/th&gt;
    &lt;th&gt;Inventor&lt;/th&gt;
    &lt;th sort="float"&gt;Price&lt;/th&gt;
    &lt;th sort="date"&gt;Expires&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr class="alt"&gt;
    &lt;td&gt;1&lt;/td&gt;
    &lt;td&gt;Thomas Edison&lt;/td&gt;
    &lt;td&gt;$13.50&lt;/td&gt;
    &lt;td&gt;13 Feb 2008&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;2&lt;/td&gt;
    &lt;td&gt;Nikolai Tesla&lt;/td&gt;
    &lt;td&gt;$21.00&lt;/td&gt;
    &lt;td&gt;4 Aug 2007&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr class="alt"&gt;
    &lt;td&gt;3&lt;/td&gt;
    &lt;td&gt;James Harrison&lt;/td&gt;
    &lt;td&gt;$13.27&lt;/td&gt;
    &lt;td&gt;25 Dec 2007&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;

&lt;script src="http://www.inventivelabs.com.au/static/files/assets/6157/sorttable.js"&gt;&lt;/script&gt;

&lt;p&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;small&gt;* Obviously, this may not work within your feed reader.&lt;/small&gt;&lt;/p&gt;

&lt;pre style="display:none"&gt;
&lt;style&gt;
a.download img {
  vertical-align: middle;
  display: none;
}

table.sortable {
  border-collapse: collapse;
}

table.sortable th {
  background: #CCC;
  border-bottom: 1px solid #999;
  border-right: 1px solid #999;
  text-align: left;
  color: black;
  cursor: pointer;
}
table.sortable th, table.sortable td {
  padding: 2px 6px;
}

table.sortable td {
  font-size: 85%;
}

table.sortable tr.alt {
  background: #003569;
}
&lt;/style&gt;
&lt;/pre&gt;</content>
    </entry>
  
    <entry>
      <title>Not bad for a one-year-old</title>
      <link href="http://inventivelabs.com.au:80/weblog/post/not-bad-for-a-one-year-old/" rel="alternate" />
      <id>/weblog/post/not-bad-for-a-one-year-old/</id>
      <updated>
        2007-07-02T15:24:55Z
      </updated>
      <author>
        <name>Virginia Murdoch</name>
      </author>
      <content type="html">&lt;p&gt;Yesterday, Inventive Labs was one year old. Rough calculations reveal that in the last twelve months we:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wrote 41 proposals&lt;/li&gt;
&lt;li&gt;Had 48 meetings with clients, including one &lt;a href="http://en.wikipedia.org/wiki/Malcolm_Fraser"&gt;former Prime Minister of Australia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Designed &lt;a href="http://inventivelabs.com.au:80/portfolio"&gt;14 websites&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Built 8 web applications, including 5 for clients and 3 for use in-house&lt;/li&gt;
&lt;li&gt;Invented &lt;a href="http://inventivelabs.com.au:80/blueprint/"&gt;Blueprint&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Made 2,196 check-ins to our &lt;a href="http://subversion.tigris.org/"&gt;Subversion&lt;/a&gt; repository&lt;/li&gt;
&lt;li&gt;Had 19 'business meetings', most of them at The Gem in Collingwood&lt;/li&gt;
&lt;li&gt;Ate 26 pizzas from Bomb in Abbotsford&lt;/li&gt;
&lt;li&gt;Rose to ridiculous heights in Wii Tennis (Joseph)&lt;/li&gt;
&lt;li&gt;Rose to moderate heights in Wii Tennis (Virginia)&lt;/li&gt;
&lt;li&gt;Cursed Internet Explorer 234,511 times, mostly while wrangling the alpha transparency for this site&lt;/li&gt;
&lt;li&gt;Praised &lt;a href="http://www.capify.org/"&gt;Capistrano&lt;/a&gt; 1,200 times&lt;/li&gt;
&lt;li&gt;Drank the odd beer on a Friday afternoon&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We also met our financial targets (modest and unscientific as they were), worked with and for a host of excellent people, and never for a second regretted our leap into the Unknown Unknowns of business ownership. Not bad for a one year-old.&lt;/p&gt;</content>
    </entry>
  
    <entry>
      <title>iPhone development bookmarklets</title>
      <link href="http://inventivelabs.com.au:80/weblog/post/iphone-development-bookmarklets/" rel="alternate" />
      <id>/weblog/post/iphone-development-bookmarklets/</id>
      <updated>
        2007-06-27T16:58:54Z
      </updated>
      <author>
        <name>Joseph</name>
      </author>
      <content type="html">&lt;h3&gt;Part 1, where I go off on a tangent.&lt;/h3&gt;

&lt;p&gt;There's a mad techno-media lather working up over the launch of the iPhone, like someone dumped a shipload of hype concentrate into the oceans of geekery. Our office houses &lt;a href="http://www.alertbutnotalarmed.com"&gt;at least one particularly fervent Apple watcher&lt;/a&gt; and thus we're all  immersed in the bubbles, despite being on the other side of the world to every iPhone in existence right now (and, sadly, for the forseeable future). &lt;/p&gt;

&lt;p&gt;This post does have some genuine relevance to Apple's &lt;a href="http://gizmodo.com/gadgets/cellphones/the-pope-says-worship-not-false-idols-save-us-oh-true-jesus-phone-224143.php"&gt;Jesus phone&lt;/a&gt;, but first I have to tell the story. &lt;/p&gt;

&lt;p&gt;While we were developing this site, I scrapped together a couple of bookmarklets to help us compose screengrabs for our portfolio. Virginia had provided me with pixel-precise dimensions for the images (667x225, fyi), and I thought it would be useful to frame the shots in the browser before copying the screen to the clipboard for cropping.&lt;/p&gt;

&lt;p&gt;What I wanted was a bookmarklet that set the viewport of my browser to exactly 667x225 pixels. This is different from setting the browser window to specific dimensions — what I wanted was the &lt;em&gt;visible page area&lt;/em&gt; to be a certain size.&lt;/p&gt;

&lt;p&gt;Which is something of an idiosyncratic situation, I suppose. Anyway, I ended up writing both bookmarklets, available below. Drag and drop them to your browser toolbar.&lt;/p&gt;

&lt;p&gt;&lt;a href="javascript:(function(){var%20x=1024;var%20y=768;var%20dims=prompt(%22Window%20dimensions:%22,x+%22x%22+y);dims=dims.split('x');x=parseInt(dims[0]);y=parseInt(dims[1]);if(!x||!y){return;}self.resizeTo(x,y);})()"&gt;Resize Window (prompt)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="javascript:(function(){var%20x=320;var%20y=480;var%20dims=prompt(%22Viewport%20dimensions:%22,x+%22x%22+y);if(!dims){return;}dims=dims.split('x');x=parseInt(dims[0]);y=parseInt(dims[1]);if(!x||!y){return;}x+=self.outerWidth-self.innerWidth;y+=self.outerHeight-self.innerHeight;window.resizeTo(x,y);if(navigator.userAgent.indexOf('AppleWebKit/4')%3E-1){return;}x+=window.innerWidth-document.documentElement.clientWidth;y+=window.innerHeight-document.documentElement.clientHeight;window.resizeTo(x,y);})()"&gt;Resize Viewport (prompt)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that Render Viewport won't work in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IE — didn't have a need, and IE's bookmarklet support is woeful&lt;/li&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.quirksmode.org/css/quirksmode.html"&gt;Quirks mode&lt;/a&gt; — too many quirks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h3&gt;Part 2, where I bring it all back to the device &lt;em&gt;du jour&lt;/em&gt;.&lt;/h3&gt;

&lt;p&gt;So, the iPhone. A lot of folks are going to be making apps specifically for, or with a special compatibility mode for, the iPhone. &lt;img alt="iphone" class="neg_column" src="http://inventivelabs.com.au:80/static/files/assets/6085/iphone.jpg" /&gt; Now the iPhone's Safari has pretty good support for displaying the 800x600+ layouts on which the web floats its boat, but still, you won't always want to force your iPhone users to zoom each time they use your application. You'll want to give them something that works at 320x480 (or 480x320). And for convenience, you'll want to preview it on your development machines, running Safari or Firefox. Which means you're going to want these:&lt;/p&gt;

&lt;p&gt;&lt;a href="javascript:(function(){var%20x=320;var%20y=416;x+=self.outerWidth-self.innerWidth;y+=self.outerHeight-self.innerHeight;window.resizeTo(x,y);if(navigator.userAgent.indexOf('AppleWebKit/4')%3E-1){return;}x+=window.innerWidth-document.documentElement.clientWidth;y+=window.innerHeight-document.documentElement.clientHeight;window.resizeTo(x,y);})()"&gt;Resize Viewport (iPhone vertical)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="javascript:(function(){var%20x=480;var%20y=270;x+=self.outerWidth-self.innerWidth;y+=self.outerHeight-self.innerHeight;window.resizeTo(x,y);if(navigator.userAgent.indexOf('AppleWebKit/4')%3E-1){return;}x+=window.innerWidth-document.documentElement.clientWidth;y+=window.innerHeight-document.documentElement.clientHeight;window.resizeTo(x,y);})()"&gt;Resize Viewport (iPhone horizontal)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note: I've extrapolated the viewport dimensions by multiplying the ratios of the Safari chrome in published screenshots against the known screen resolution of the display. In both modes, the status bar is 18 pixels high; in vertical mode the toolbar appears to be 46px and horizontal it's 32px. So, I'm going on &lt;strong&gt;320x416&lt;/strong&gt; for vertical and &lt;strong&gt;480x270&lt;/strong&gt; for horizontal. I might be out by a pixel or two—if so, let me know and I'll update accordingly.&lt;/p&gt;</content>
    </entry>
  
    <entry>
      <title>A conundrum</title>
      <link href="http://inventivelabs.com.au:80/weblog/post/a-conundrum/" rel="alternate" />
      <id>/weblog/post/a-conundrum/</id>
      <updated>
        2007-06-14T16:56:53Z
      </updated>
      <author>
        <name>Joseph</name>
      </author>
      <content type="html">&lt;p&gt;How can a web development company stay in business for a year without a proper website?&lt;/p&gt;

&lt;p&gt;It became a bit of a running joke down here in the laboratories, but the truth is (like our placeholder site said): we've been busy. &lt;a href="http://inventivelabs.com.au:80/portfolio"&gt;Really busy&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Nevertheless, with the end of Year One In Business fast approaching, we cleared a little room between our various projects to knock something together, and here it is. &lt;img alt="cards" class="neg_column" src="http://inventivelabs.com.au:80/static/files/assets/6103/cards_crop.jpg" /&gt;  In the unlovely expression of the industry, we're eating our own dog food—this site is powered by Blueprint, our delightfully simple and endlessly flexible Content Management System. &lt;/p&gt;

&lt;p&gt;Anyway, this is our 'Hello World' post. Stick around—there's &lt;em&gt;way&lt;/em&gt; more interesting stuff to come.&lt;/p&gt;</content>
    </entry>
  
</feed>
