<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Otaqui.Com</title>
	<atom:link href="http://otaqui.com/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://otaqui.com/blog</link>
	<description>Pete Otaqui's blog about web development and everything else</description>
	<lastBuildDate>Mon, 08 Mar 2010 12:15:24 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Custom User Agent with Cucumber Tests and Webrat in Mechanize Mode</title>
		<link>http://otaqui.com/blog/541/custom-user-agent-with-cucumber-tests-and-webrat-in-mechanize-mode/</link>
		<comments>http://otaqui.com/blog/541/custom-user-agent-with-cucumber-tests-and-webrat-in-mechanize-mode/#comments</comments>
		<pubDate>Mon, 08 Mar 2010 12:08:50 +0000</pubDate>
		<dc:creator>pete</dc:creator>
				<category><![CDATA[Professional]]></category>
		<category><![CDATA[cucumber]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[testing]]></category>
		<category><![CDATA[webdev]]></category>

		<guid isPermaLink="false">http://otaqui.com/blog/?p=541</guid>
		<description><![CDATA[It&#8217;s pretty straightforward to set a custom user agent with mechanize if you&#8217;re using it directly, KickAssLabs has a good example.
If you&#8217;re using Mechanize through Webrat though, things are a little different.  The nice thing though, is that you can do this in a step definition &#8211; allowing features to be based on different [...]]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s pretty straightforward to set a custom user agent with mechanize if you&#8217;re using it directly, <a href="http://www.kickasslabs.com/2009/03/31/quick-hits-setting-the-user-agent-header-in-webrat/">KickAssLabs has a good example</a>.</p>
<p>If you&#8217;re using Mechanize through Webrat though, things are a little different.  The nice thing though, is that you can do this in a step definition &#8211; allowing features to be based on different browser scenarios (this is great if you&#8217;re developing across devices).</p>
<p>First off there are two accessors of interest in mechanize, &#8220;user_agent&#8221; and &#8220;user_agent_alias&#8221;.  The former is fairly self evident and lets you define the user agent at will, the latter gives you shortcut access to a list of common user agents baked into mechanize.  You can see that list in <a href="http://github.com/tenderlove/mechanize/blob/master/lib/mechanize.rb#L48">the mechanize.rb source</a>.  It contains some desktop browsers, the iPhone and the default Mechanize UA strings.</p>
<p>If you&#8217;re happy with the default alias list, you can just write a step definition like this:</p>
<pre class="brush: ruby">
Given /^I am using the &quot;(.*)&quot; browser$/ do |browser|
  webrat.adapter.mechanize.user_agent_alias = browser
end
</pre>
<p>And then you can use this in your features:</p>
<pre class="brush: text">
Given I am using the &quot;iPhone&quot; browser
When I visit the home page
Then I should see &quot;You are using an iPhone!&quot;
</pre>
<p>If you want to be able to specify your own UA strings, and as I said this is especially likely if you are developing cross-device applications where capabilities are important, then you can just use your own hash instead, and set &#8220;user_agent&#8221; rather than &#8220;user_agent_alias&#8221;:</p>
<pre class="brush: ruby">
Given /^I am using the &quot;(.*)&quot; browser$/ do |browser|
  UA_ALIASES = {
    &quot;Nokia N95&quot; =&gt; &quot;Mozilla/5.0 (SymbianOS/9.2; .....&quot;,
    &quot;Palm Pre&quot; =&gt; &quot;Mozilla/5.0 (webOS/1.0; .....&quot;
  }
  webrat.adapter.mechanize.user_agent = UA_ALIASES[browser]
end
</pre>
<p>With a little tweaking you could use both the included list and, if the string from your feature isn&#8217;t there, look in a custom list too.</p>
]]></content:encoded>
			<wfw:commentRss>http://otaqui.com/blog/541/custom-user-agent-with-cucumber-tests-and-webrat-in-mechanize-mode/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Complete List of Special Google Chrome URLs</title>
		<link>http://otaqui.com/blog/539/complete-list-of-special-google-chrome-urls/</link>
		<comments>http://otaqui.com/blog/539/complete-list-of-special-google-chrome-urls/#comments</comments>
		<pubDate>Fri, 05 Mar 2010 20:12:54 +0000</pubDate>
		<dc:creator>pete</dc:creator>
				<category><![CDATA[Professional]]></category>
		<category><![CDATA[browser]]></category>
		<category><![CDATA[webdev]]></category>

		<guid isPermaLink="false">http://otaqui.com/blog/?p=539</guid>
		<description><![CDATA[Google Chrome has quite a lot of special URLs.  Some of these are covered over at Lifehacker in their post on Chrome&#8217;s about pages, but the meta &#8220;about:&#8221; protocol isn&#8217;t the only one that Chrome uses.
Waha, a user on the chromeplugins.org site posted a much more complete list of URL schemes that Chrome uses. [...]]]></description>
			<content:encoded><![CDATA[<p>Google Chrome has quite a lot of special URLs.  Some of these are covered over at Lifehacker in their <a href="http://lifehacker.com/5045164/google-chromes-full-list-of-special-about-pages">post on Chrome&#8217;s about pages</a>, but the meta &#8220;about:&#8221; protocol isn&#8217;t the only one that Chrome uses.</p>
<p>Waha, a user on the chromeplugins.org site posted <a href="http://www.chromeplugins.org/google/chrome-tips-tricks/about-chrome-more-internal-urls-7793.html">a much more complete list of URL schemes</a> that Chrome uses.  Waha was looking at the <a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/url_constants.cc">url_constants.cc</a> source file, along with some other stuff, to work this list out.</p>
<p>I&#8217;ve ignored the virtually universal set of protocols that are in just about every browser: http, https, ftp, file, data, feed and javascript, and also the URLs that cannot be entered into the address bar (not using Chrome 5 on OS X Snow Leopard anyway).</p>
<p>Without further ado, here&#8217;s the list:</p>
<ul>
<li><a href="about:blank">about:blank</a> &#8211; the empty page</li>
<li><a href="about:cache">about:cache</a> &#8211; disk and memory cache information</li>
<li><a href="about:net-internals">about:net-internals</a> &#8211; network information including Proxy, HostResolver, URLRequest, HTTPCache and SocketStream</li>
<li><a href="about:crash">about:crash</a> &#8211; the page shown when a tab process crashes</li>
<li><a href="about:credits">about:credits</a> &#8211; list of libraries and other code used in Chrome, with links</li>
<li>about:hang &#8211; this seems to kill a tab for me (I haven&#8217;t linked for that reason &#8211; use at your own risk)</li>
<li><a href="about:memory">about:memory</a> &#8211; memory usage of the various processes</li>
<li>about:shorthang &#8211; see about:hang above</li>
<li><a href="about:terms">about:terms</a> &#8211; Google Chrome Terms of Service</li>
<li>about:inducebrowsercrashforrealz &#8211; not many lolz here</li>
<li><a href="chrome://extensions/">chrome://extensions/</a> &#8211; installed extensions</li>
<li><a href="chrome://history/">chrome://history/</a> &#8211; your browsing history</li>
<li><a href="chrome://newtab">chrome://newtab</a> &#8211; the new tab page</li>
<li><a href="chrome://thumb/http://www.google.com/">chrome://thumb/http://www.google.com/</a> &#8211; thumbnail for a page you&#8217;ve visited</li>
<li><a href="chrome://favicon/http://www.google.com/">chrome://favicon/http://www.google.com/</a> &#8211; favicon for a page you&#8217;ve visited</li>
<li><a href="view-source:http://otaqui.com/blog/539/complete-list-of-special-google-chrome-urls">view-source:http://otaqui.com/blog/539/complete-list-of-special-google-chrome-urls</a> &#8211; view the source of a web page</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://otaqui.com/blog/539/complete-list-of-special-google-chrome-urls/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Firebug 1.5 updates with cool new context-sensitive &#8220;Break On Next&#8221;</title>
		<link>http://otaqui.com/blog/529/firebug-1-5-updates-with-cool-new-context-sensitive-break-on-next/</link>
		<comments>http://otaqui.com/blog/529/firebug-1-5-updates-with-cool-new-context-sensitive-break-on-next/#comments</comments>
		<pubDate>Tue, 23 Feb 2010 15:26:59 +0000</pubDate>
		<dc:creator>pete</dc:creator>
				<category><![CDATA[Professional]]></category>

		<guid isPermaLink="false">http://otaqui.com/blog/?p=529</guid>
		<description><![CDATA[Firebug, the mighty hammer of web development, has updated to 1.5.  The coolest new feature is the &#8220;Break On Next&#8221; button is now context sensitive, depending on which panel you are looking at.  The breakdown of &#8220;break on next &#8230;&#8221; to panels is as follows:

Firebug Break on Next Specifics


Panel
Event




Console
Break on All Errors


HTML
Break on [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://getfirebug.com">Firebug</a>, the mighty hammer of web development, has updated to 1.5.  The coolest new feature is the &#8220;Break On Next&#8221; button is now context sensitive, depending on which panel you are looking at.  The breakdown of &#8220;break on next &#8230;&#8221; to panels is as follows:</p>
<table>
<caption>Firebug Break on Next Specifics</caption>
<thead>
<tr>
<th style="width:50%;">Panel</th>
<th style="width:50%;">Event</th>
</tr>
</thead>
<tbody>
<tr>
<td>Console</td>
<td>Break on All Errors</td>
</tr>
<tr>
<td>HTML</td>
<td>Break on mutate</td>
</tr>
<tr>
<td>CSS</td>
<td>Disabled</td>
</tr>
<tr>
<td>Script</td>
<td>Break on Next statement</td>
</tr>
<tr>
<td>DOM</td>
<td>Disabled</td>
</tr>
<tr>
<td>Net</td>
<td>Break on XHR</td>
</tr>
</tbody>
</table>
<p>It&#8217;s really great to see this extension continue to improve not only in terms of stability and useful features without getting bloated.</p>
<p>The original note about this feature is on the <a href="http://blog.getfirebug.com/2010/02/16/firebug-1-6a5/">Firebug 1.6a5 blog post</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://otaqui.com/blog/529/firebug-1-5-updates-with-cool-new-context-sensitive-break-on-next/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>BBC Standards Ruby Gem</title>
		<link>http://otaqui.com/blog/526/bbc-standards-ruby-gem/</link>
		<comments>http://otaqui.com/blog/526/bbc-standards-ruby-gem/#comments</comments>
		<pubDate>Wed, 17 Feb 2010 22:19:52 +0000</pubDate>
		<dc:creator>pete</dc:creator>
				<category><![CDATA[Professional]]></category>
		<category><![CDATA[bbc]]></category>
		<category><![CDATA[blogging]]></category>
		<category><![CDATA[cucumber]]></category>
		<category><![CDATA[webdev]]></category>

		<guid isPermaLink="false">http://otaqui.com/blog/?p=526</guid>
		<description><![CDATA[Thanks to some brilliant tidying up of my work by my good friend <a href="http://www.metade.org/">Patrick</a>, there is now a <a href="http://github.com/metade/bbc_standards">bbc_standards ruby gem</a> which you can use to validate pages against some of the <a href="http://www.bbc.co.uk/guidelines/futuremedia/technical/">BBC Technical Guidelines</a>]]></description>
			<content:encoded><![CDATA[<p>Thanks to some brilliant tidying up of my work by my good friend <a href="http://www.metade.org/">Patrick</a>, there is now a <a href="http://github.com/metade/bbc_standards">bbc_standards ruby gem</a> which you can use to validate pages against some of the <a href="http://www.bbc.co.uk/guidelines/futuremedia/technical/">BBC Technical Guidelines</a> (more specifically the &#8220;Semantic Mark-up&#8221; and &#8220;XHTML Integrity&#8221; standards).</p>
<p>This gem was originally designed to work with Cucumber, and initially was intended to just validate pages against the XHTML Strict doctype that BBC pages are usually required to use.  Since it uses Nokogiri for validation &#8211; against the XML Schema rather than the Doctype for what it&#8217;s worth &#8211; the testing is a) strict and b) *fast*.  There are no external webservice calls, so it is fine to test all page loads within Cucumber, even testing the same URI many times over (since the bottleneck will almost certainly be your app by some margin.</p>
<p>I&#8217;m currently working on a post about using the gem for the <a href="http://www.bbc.co.uk/blogs/webdeveloper/">BBC Web Developer blog</a>, but I thought I&#8217;d point any interested parties at the code here and now.</p>
]]></content:encoded>
			<wfw:commentRss>http://otaqui.com/blog/526/bbc-standards-ruby-gem/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Create A Custom CakePHP Console Application Using Shells, Tasks, Models and Controllers</title>
		<link>http://otaqui.com/blog/448/create-a-custom-cakephp-console-application-using-shells-tasks-models-and-controllers/</link>
		<comments>http://otaqui.com/blog/448/create-a-custom-cakephp-console-application-using-shells-tasks-models-and-controllers/#comments</comments>
		<pubDate>Wed, 10 Feb 2010 14:43:54 +0000</pubDate>
		<dc:creator>pete</dc:creator>
				<category><![CDATA[Professional]]></category>
		<category><![CDATA[cakephp]]></category>
		<category><![CDATA[webdev]]></category>

		<guid isPermaLink="false">http://otaqui.com/blog/?p=448</guid>
		<description><![CDATA[CakePHP doesn&#8217;t just come with its own console applications for baking code, managing ACL, inspecting classes, manipulating the schema, internationalisation and running the testsuite (whew!) it also lets you write your own console applications.
[NB: I always have an external cake core directory, for easy swapping.  The code examples all assume you have done this, [...]]]></description>
			<content:encoded><![CDATA[<p>CakePHP doesn&#8217;t just come with its own console applications for baking code, managing ACL, inspecting classes, manipulating the schema, internationalisation and running the testsuite (whew!) it also lets you write your own console applications.</p>
<p><em>[NB: I always have an external cake core directory, for easy swapping.  The code examples all assume you have done this, or at least created an alias in your terminal to point to the core cake directory with something like: alias cake='../cake/console/cake'</em></p>
<p>First off, to find out about the current console applications you are running jump into a terminal and cd to your app directory, then simple run the &#8220;cake&#8221; command to see what you have available:</p>
<pre class="brush: bash">
app_dir $ cake
</pre>
<p>This will output a list of available shells in the core cake library and the two &#8220;vendors&#8221; directories.</p>
<p>So how do you go about creating your own?  The <a href="http://book.cakephp.org/view/110/Creating-Shells-Tasks">CakePHP documentation on creating Shells &amp; Tasks</a> is a good place to start.</p>
<p>An application I&#8217;ve needed to automate with a cron-job, and also have available as an in-app, on-demand function is the sending of emails.  I&#8217;m not talking about writing a mass mailer here, but rather just sending out course information to users who are booked to attend &#8211; and doing so both automatically and on-demand when an administrator decides it is necessary.  This is an obvious instance where you don&#8217;t want to duplicate the code in the controller and the shell.</p>
<p>In my case, I have several kinds of email I want to send, so I set up a generalised Task, used by several different Shells.  The advantage of doing things this way around is that any other Shells can also make use of the emailer Task in the fututre.</p>
<p>Here&#8217;s some simplified code from one of the Shells, a &#8220;booking reminder&#8221; for delegates:</p>
<pre class="brush: php">
// file app/vendors/shells/booking_reminder.php
&lt;?php
class BookingReminderShell extends Shell {
  var $uses = array(&quot;Booking&quot;,&quot;User&quot;,&quot;Course&quot;);
  var $tasks = array(&quot;Emailer&quot;);
  public function main() {
    // use $this-&gt;Booking etc as if we were in a controller
    // use $this-&gt;Emailer for the task
  }
}
</pre>
<p>And then we have the Task:</p>
<pre class="brush: php">
// file app/vendors/shells/tasks/emailer.php
&lt;?php
class Emailer extends Shell {
  public function execute() {
    // this method is called when the task is instantiated,
    // gives you a chance to setup
  }
  public function send($to,$from,$subject,$message,$attachments=null) {
    // called from shell with: $this-&gt;Emailer-&gt;send();
  }
}
</pre>
<p>Note how both Shells and tasks extend from the base &#8220;Shell&#8221; class.  Also note how Shells use a method called &#8220;main()&#8221; when they are run, whereas Tasks use &#8220;execute()&#8221; when they are created.</p>
]]></content:encoded>
			<wfw:commentRss>http://otaqui.com/blog/448/create-a-custom-cakephp-console-application-using-shells-tasks-models-and-controllers/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Comment And Uncomment All Lines in a Linux File with Sed</title>
		<link>http://otaqui.com/blog/460/comment-and-uncomment-all-lines-in-a-linux-file-with-sed/</link>
		<comments>http://otaqui.com/blog/460/comment-and-uncomment-all-lines-in-a-linux-file-with-sed/#comments</comments>
		<pubDate>Tue, 09 Feb 2010 09:21:27 +0000</pubDate>
		<dc:creator>pete</dc:creator>
				<category><![CDATA[Professional]]></category>
		<category><![CDATA[command-line]]></category>
		<category><![CDATA[mac]]></category>

		<guid isPermaLink="false">http://otaqui.com/blog/?p=460</guid>
		<description><![CDATA[Just a couple of sed one-liners for adding and removing comments in the form of # marks (in the case of my ~/.ssh/config file).  I use this to toggle proxy settings (with connect.c) and it&#8217;s run by the awesome MarcoPolo location-aware app for OS X.  Both of these are safe to run repeatedly [...]]]></description>
			<content:encoded><![CDATA[<p>Just a couple of sed one-liners for adding and removing comments in the form of # marks (in the case of my ~/.ssh/config file).  I use this to toggle proxy settings (with connect.c) and it&#8217;s run by the awesome MarcoPolo location-aware app for OS X.  Both of these are safe to run repeatedly (you won&#8217;t end up with multiple # marks or anything).</p>
<p>First adding comments, which means a &#8220;#&#8221; mark at the start of every line<br />
<code>sed -i '' 's/^\([^#]\)/#\1/g' ~/.ssh/config</code></p>
<p>Second removing the comments, just stripping out the &#8220;#&#8221; marks.<br />
<code>sed -i '' 's/^#//g' ~/.ssh/config</code></p>
<p>A few things to note here, on the assumption that you can see how Regular Expressions work and are vaguely familiar with sed&#8217;s /find/replace/ style syntax:</p>
<ol>
<li>the &#8220;-i&#8221; flag means &#8220;edit in place&#8221; and requires an extra argument for the backup file&#8217;s extension.  I&#8217;ve given an empty string so that no backup is made</li>
<li>sed requires escaping of capturing parentheses, hence the \( and \) in the first example</li>
<li>the final &#8216;g&#8217; at the end of the expressions means &#8220;global&#8221;, i.e. replace all occurences</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://otaqui.com/blog/460/comment-and-uncomment-all-lines-in-a-linux-file-with-sed/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Using a Proxy with Cucumber, Webrat and Mechanize</title>
		<link>http://otaqui.com/blog/458/using-a-proxy-with-cucumber-webrat-and-mechanize/</link>
		<comments>http://otaqui.com/blog/458/using-a-proxy-with-cucumber-webrat-and-mechanize/#comments</comments>
		<pubDate>Thu, 17 Dec 2009 08:15:36 +0000</pubDate>
		<dc:creator>pete</dc:creator>
				<category><![CDATA[Professional]]></category>
		<category><![CDATA[cucumber]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[webdev]]></category>

		<guid isPermaLink="false">http://otaqui.com/blog/?p=458</guid>
		<description><![CDATA[If you&#8217;re writing Cucumber tests using Webrat and Mechanize to test a site, and you are behind a proxy server, you can do something like this to tell mechanize about it in your webrat_steps.rb file:

When /^I am on (.+)$/ do &#124;page_name&#124;
    webrat.adapter.mechanize.set_proxy('proxy.host.com',8080)
    visit path_to(page_name)
end

I&#8217;m sure there&#8217;s a tidier way [...]]]></description>
			<content:encoded><![CDATA[<p>If you&#8217;re writing Cucumber tests using Webrat and Mechanize to test a site, and you are behind a proxy server, you can do something like this to tell mechanize about it in your webrat_steps.rb file:</p>
<p><code><br />
When /^I am on (.+)$/ do |page_name|<br />
    webrat.adapter.mechanize.set_proxy('proxy.host.com',8080)<br />
    visit path_to(page_name)<br />
end<br />
</code></p>
<p>I&#8217;m sure there&#8217;s a tidier way to do this, but it&#8217;s quick and it works with the following gem versions:</p>
<p>Cucumber 0.3.11<br />
Webrat 0.5.3<br />
Mechanize 0.9.3</p>
]]></content:encoded>
			<wfw:commentRss>http://otaqui.com/blog/458/using-a-proxy-with-cucumber-webrat-and-mechanize/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Cucumber with Webrat and Mechanize on CentOS 5</title>
		<link>http://otaqui.com/blog/449/cucumber-with-webrat-and-mechanize-on-centos-5/</link>
		<comments>http://otaqui.com/blog/449/cucumber-with-webrat-and-mechanize-on-centos-5/#comments</comments>
		<pubDate>Fri, 20 Nov 2009 13:55:38 +0000</pubDate>
		<dc:creator>pete</dc:creator>
				<category><![CDATA[Professional]]></category>
		<category><![CDATA[command-line]]></category>
		<category><![CDATA[cucumber]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://otaqui.com/blog/?p=449</guid>
		<description><![CDATA[Thanks to Chris for this one.
The trick is to use JRuby and its gems, and also to install libxml2-devel
$ cd ~
$ wget http://jruby.kenai.com/downloads/1.4.0/jruby-bin-1.4.0.tar.gz
$ tar -C /usr/local/ -xzvf jruby-bin-1.4.0.tar.gz
You should now have Jruby.  Add it&#8217;s bin directory to your path, presumably in your ~/.bash_profile file:
$ export PATH=$PATH:/usr/local/jruby-1.4.0/bin
$ jruby -v
Now install some mechanize requirements:
$ sudo yum [...]]]></description>
			<content:encoded><![CDATA[<p>Thanks to <a href="http://blog.chrislowis.co.uk/">Chris</a> for this one.</p>
<p>The trick is to use JRuby and its gems, and also to install libxml2-devel</p>
<p><code>$ cd ~<br />
$ wget http://jruby.kenai.com/downloads/1.4.0/jruby-bin-1.4.0.tar.gz<br />
$ tar -C /usr/local/ -xzvf jruby-bin-1.4.0.tar.gz</code></p>
<p>You should now have Jruby.  Add it&#8217;s bin directory to your path, presumably in your ~/.bash_profile file:<br />
<code>$ export PATH=$PATH:/usr/local/jruby-1.4.0/bin<br />
$ jruby -v</code></p>
<p>Now install some mechanize requirements:<br />
<code>$ sudo yum install libxml2-devel libxslt-devel</code></p>
<p>If you <strong>don&#8217;t</strong> already have the &#8220;normal&#8221; ruby installed, you can get to the jruby &#8220;gem&#8221; command by just typing &#8220;gem&#8221;.  If you do have ruby, or want to be absolutely sure, type the commands like this:<br />
<code>$ jruby -S gem install cucumber mechanize webrat</code></p>
<p>You&#8217;re good to go!  You should now be able to run your features with:</p>
<p><code>$ cucumber features/</code></p>
]]></content:encoded>
			<wfw:commentRss>http://otaqui.com/blog/449/cucumber-with-webrat-and-mechanize-on-centos-5/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>CakePHP&#8217;s basics.php functions &#8211; a series of really useful shortcut functions available globally in your CakePHP app</title>
		<link>http://otaqui.com/blog/443/cakephps-basicsphp-functions-a-series-of-really-useful-shortcut-functions-available-globally-in-your-cakephp-app/</link>
		<comments>http://otaqui.com/blog/443/cakephps-basicsphp-functions-a-series-of-really-useful-shortcut-functions-available-globally-in-your-cakephp-app/#comments</comments>
		<pubDate>Tue, 24 Feb 2009 14:07:30 +0000</pubDate>
		<dc:creator>pete</dc:creator>
				<category><![CDATA[Professional]]></category>
		<category><![CDATA[cakephp]]></category>

		<guid isPermaLink="false">http://otaqui.com/blog/?p=443</guid>
		<description><![CDATA[If you are working with CakePHP and haven&#8217;t checked it out yet, it&#8217;s very well worth looking through basics.php in the API docs.
As well as adding some PHP 5 functionality on PHP 4 servers, the script also adds a fair number of utility functions to the global namespace, such as:


// similar to print_r(), but dumps [...]]]></description>
			<content:encoded><![CDATA[<p>If you are working with <a href="http://cakephp.org">CakePHP</a> and haven&#8217;t checked it out yet, it&#8217;s very well worth looking through <a href="http://api.cakephp.org/view_source/basics.php">basics.php in the API docs</a>.</p>
<p>As well as adding some PHP 5 functionality on PHP 4 servers, the script also adds a fair number of utility functions to the global namespace, such as:</p>
<pre class="brush: php">

// similar to print_r(), but dumps to the view and
// is disabled if debug isn&#039;t &gt; 0 in config.php
pr($anything); 

// returns the correctly internationalised plural or
// singular depending on locale.
__n($singular, $plural, $count, $return);
</pre>
]]></content:encoded>
			<wfw:commentRss>http://otaqui.com/blog/443/cakephps-basicsphp-functions-a-series-of-really-useful-shortcut-functions-available-globally-in-your-cakephp-app/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Syncing mail.app with gmail and the iphone using imap</title>
		<link>http://otaqui.com/blog/440/syncing-mailapp-with-gmail-and-the-iphone-using-imap/</link>
		<comments>http://otaqui.com/blog/440/syncing-mailapp-with-gmail-and-the-iphone-using-imap/#comments</comments>
		<pubDate>Tue, 24 Feb 2009 11:13:17 +0000</pubDate>
		<dc:creator>pete</dc:creator>
				<category><![CDATA[Professional]]></category>
		<category><![CDATA[google]]></category>
		<category><![CDATA[iphone]]></category>
		<category><![CDATA[mac]]></category>

		<guid isPermaLink="false">http://otaqui.com/blog/440/syncing-mailapp-with-gmail-and-the-iphone-using-imap/</guid>
		<description><![CDATA[I&#8217;ve set up my google apps email account using imap in mail.app and it&#8217;s working quite well in terms of keeping track of read and unread emails and having my sent emails available everywhere.
It&#8217;s definitely worth going one step beyond google&#8217;s own instructions on the matter and correctly configuring the drafts, sent and trash folders [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve set up my google apps email account using imap in mail.app and it&#8217;s working quite well in terms of keeping track of read and unread emails and having my sent emails available everywhere.</p>
<p>It&#8217;s definitely worth going one step beyond google&#8217;s own instructions on the matter and correctly configuring the drafts, sent and trash folders in both clients:</p>
<h3>Mail.app</h3>
<p>Choose the google folder for drafts in the sidebar. From the os x menu bar at the top of your screen choose &#8216;mailbox&#8217; and then &#8216;use this mailbox for&#8217; and then &#8216;drafts&#8217;. Do the same with the other folders.</p>
<h3>iPhone Mail</h3>
<p>Having done the basic setup, go to the settings app, choose &#8216;mail, contacts and calendars&#8217;, navigate to your mail account and choose &#8216;advanced&#8217; and then setup the &#8216;mailbox behaviours&#8217; as required.</p>
<p><b>NB</b> if you are a fan of &#8216;labels&#8217; in gmail, bear in mind that these equate to <b>copies</b> of the email in IMAP folders. That means that if you have, as I did, several thousand labelled emails from a different account you will have more than one copy of each downloaded to mail.app, assuming you chose to download your messages- and who wouldn&#8217;t?</p>
<p>The iPhone seems to work pretty flawlessly, but mail.app sometimes seems to take a while to catch up with changes (especially emails you&#8217;ve read on the iPhone while mail.app was offline) although it always seems to get there in the end.</p>
]]></content:encoded>
			<wfw:commentRss>http://otaqui.com/blog/440/syncing-mailapp-with-gmail-and-the-iphone-using-imap/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>David Allen&#8217;s Getting Things Done on the iPhone, synced with iCal</title>
		<link>http://otaqui.com/blog/437/david-allens-getting-things-done-on-the-iphone-synced-with-ical/</link>
		<comments>http://otaqui.com/blog/437/david-allens-getting-things-done-on-the-iphone-synced-with-ical/#comments</comments>
		<pubDate>Mon, 23 Feb 2009 08:01:17 +0000</pubDate>
		<dc:creator>pete</dc:creator>
				<category><![CDATA[Professional]]></category>
		<category><![CDATA[gtd]]></category>
		<category><![CDATA[iphone]]></category>
		<category><![CDATA[mac]]></category>

		<guid isPermaLink="false">http://otaqui.com/blog/?p=437</guid>
		<description><![CDATA[If you are a fan of GTD, use a Mac and an iPhone, then there&#8217;s a great pair of apps you can get that seamlessly sync all your projects and tasks, and that will update your iCal while you&#8217;re at it.
Things from Cultured Code also comes as an iPhone app, and the duo update each [...]]]></description>
			<content:encoded><![CDATA[<p>If you are a fan of GTD, use a Mac and an iPhone, then there&#8217;s a great pair of apps you can get that seamlessly sync all your projects and tasks, and that will update your iCal while you&#8217;re at it.</p>
<p><a href="http://culturedcode.com/things/">Things</a> from Cultured Code also comes as an <a href="http://culturedcode.com/things/iphone">iPhone app</a>, and the duo update each other fairly well.  They miss out on being being &#8220;excellent&#8221; because the iPhone-to-mac syncing only happens when both your devices are on the same wifi network, and short of closing one of the pair and re-opening it, I can&#8217;t fnd a way of forcing a sync to happen whenever you would like.</p>
<p>That being said, they do both work extremely well.  The desktop version has a very nice quick-entry interface which feels a little bit like <a href="http://docs.blacktree.com/quicksilver/what_is_quicksilver">Quicksilver</a> (and is accessed by pressing Ctrl+Alt+Space) which is a nice touch.  Both apps use &#8220;dock badge&#8221; numbers although the number shown can only be customised on the desktop version which lets you set the number as Due, Due+Today and Due+Today+Inbox; the iPhone dock badge will only show Due+Today so personally although I would like the full count I leave the setting so that both are the same.</p>
<p>Neither app is free.  The desktop client is (at the current exchange rate) &pound;37.13 ($49.95) and you can buy Things from the App Store for &pound;5.99 ($7.99).  The desktop version is also available as a 5-license bundle for &pound;55.72 ($74.95)</p>
]]></content:encoded>
			<wfw:commentRss>http://otaqui.com/blog/437/david-allens-getting-things-done-on-the-iphone-synced-with-ical/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Fix The &#8220;For Attribute Resets Focus on Select Tag&#8221; Bug In Internet Explorer using Prototype</title>
		<link>http://otaqui.com/blog/435/fix-the-for-attribute-resets-focus-on-select-tag-bug-in-internet-explorer-using-prototype/</link>
		<comments>http://otaqui.com/blog/435/fix-the-for-attribute-resets-focus-on-select-tag-bug-in-internet-explorer-using-prototype/#comments</comments>
		<pubDate>Tue, 17 Feb 2009 10:20:36 +0000</pubDate>
		<dc:creator>pete</dc:creator>
				<category><![CDATA[Professional]]></category>
		<category><![CDATA[internet-explorer]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://otaqui.com/blog/?p=435</guid>
		<description><![CDATA[Browser sniffing is bad, or so the logic goes.
There are occasions though where it makes perfect sense &#8211; for example where you are fixing a known bug in a specific version of a browser.  A good example is the bug in IE 6 that resets the selected index of a Select tag that has [...]]]></description>
			<content:encoded><![CDATA[<p>Browser sniffing is <em>bad</em>, or so the logic goes.</p>
<p>There are occasions though where it makes perfect sense &#8211; for example where you are fixing a known bug in a specific version of a browser.  A good example is the bug in IE 6 that resets the selected index of a Select tag that has a label and the for attribute.  This bug doesn&#8217;t affect IE 7 or 8, or any other browser, but does make for a bad user experience if you are doing the right thing and including labels for your select tags.</p>
<p>Microsoft has <a href="http://support.microsoft.com/kb/314279">published some javascript to fix this</a> and I adapted their code to work with with the <a href="http://prototypejs.org">Prototype Javascript Library</a>.</p>
<p>This fix will look for all select tags on the page (you could adapt it to only look for those with the &#8220;for&#8221; attribute but I have a sneaking suspicion that if anything that would in fact be a bit slower) and observe the onfocusin and onfocus events as suggested in the knowledge base article.</p>
<pre class="brush: javascript">
// Select with &#039;for&#039; attribute fix for IE
Event.observe(window,&#039;load&#039;,function() {
	if ( !Prototype.Browser.IE || !(parseInt(navigator.userAgent.substring(navigator.userAgent.indexOf(&quot;MSIE&quot;)+5))==6) ) return;
	$$(&#039;select&#039;).each(function(eSelect) {
		eSelect.observe(&#039;focusin&#039;,function(e) {
			try {
				var eSrc = window.event.srcElement;
				if ( eSrc ) eSrc.tmpIndex = eSrc.selectedIndex;
			} catch(e) {}
		});
		eSelect.observe(&#039;focus&#039;,function(e) {
			try {
				var eSrc = window.event.srcElement;
				if ( eSrc ) eSrc.selectedIndex = eSrc.tmpIndex;
			} catch(e) {}
		})
	});
});
</pre>
]]></content:encoded>
			<wfw:commentRss>http://otaqui.com/blog/435/fix-the-for-attribute-resets-focus-on-select-tag-bug-in-internet-explorer-using-prototype/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Review of the Wordpress blogging clientfor the iPhone</title>
		<link>http://otaqui.com/blog/442/review-of-the-wordpress-blogging-clientfor-the-iphone/</link>
		<comments>http://otaqui.com/blog/442/review-of-the-wordpress-blogging-clientfor-the-iphone/#comments</comments>
		<pubDate>Sun, 15 Feb 2009 22:41:50 +0000</pubDate>
		<dc:creator>pete</dc:creator>
				<category><![CDATA[Personal]]></category>
		<category><![CDATA[Professional]]></category>
		<category><![CDATA[mac]]></category>

		<guid isPermaLink="false">http://otaqui.com/blog/442/review-of-the-wordpress-blogging-clientfor-the-iphone/</guid>
		<description><![CDATA[Having just got myself an iphone, I thought I&#8217;d test out the wordpress blogging client.
I must admit that although it&#8217;s ok for short posts I can&#8217;t imagine I&#8217;m going to be writing any very long posts on a touchscreen keyboard (although I am starting to get the hang of it even after only two days [...]]]></description>
			<content:encoded><![CDATA[<p>Having just got myself an iphone, I thought I&#8217;d test out the wordpress blogging client.</p>
<p>I must admit that although it&#8217;s ok for short posts I can&#8217;t imagine I&#8217;m going to be writing any very long posts on a touchscreen keyboard (although I am starting to get the hang of it even after only two days of light use). Please pardon any smelling pistakes!</p>
<p>The application is open source, which I&#8217;ll definitely appreciate if I ever get around to building an app myself.</p>
<p>You can work with multiple blogs, both self hosted and on wordpress.com. Once you have set up a blog you configure the amount of recent posts to show, I found that he default 10 was a usefaul number</p>
<p>When writing a post, you can set both the tags and choose from the current categories or create ones. Draft posts are saved locally, and when you have finished editing you can either publish immediately or at some future date. You can alternatively set the post as a draft or pending review, which will upload it to the server.</p>
<p>There is also support for adding images to posts &#8211; from the photo library or a new shot although no ability to control where in the post the images appear &#8211; you only get them defaulting to the bottom of the post.</p>
<p>Overall I would say that the app is as good as the iPhone allows.</p>
<p><a href="http://otaqui.com/blog/wp-content/uploads/p-640-480-79f21e48-1e0d-498e-be0b-e3580cc53ab9.jpeg"><img src="http://otaqui.com/blog/wp-content/uploads/p-640-480-79f21e48-1e0d-498e-be0b-e3580cc53ab9.jpeg" alt="" width="225" height="300" class="alignnone size-full wp-image-364" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://otaqui.com/blog/442/review-of-the-wordpress-blogging-clientfor-the-iphone/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Disable Debug Output for Ajax in CakePHP</title>
		<link>http://otaqui.com/blog/430/disable-debug-output-for-ajax-in-cakephp/</link>
		<comments>http://otaqui.com/blog/430/disable-debug-output-for-ajax-in-cakephp/#comments</comments>
		<pubDate>Sun, 15 Feb 2009 09:15:46 +0000</pubDate>
		<dc:creator>pete</dc:creator>
				<category><![CDATA[Professional]]></category>
		<category><![CDATA[cakephp]]></category>
		<category><![CDATA[webdev]]></category>

		<guid isPermaLink="false">http://otaqui.com/blog/?p=430</guid>
		<description><![CDATA[If you are developing a CakePHP application and you have the debug value set to anything greater than 0 in your config/core.php file, you will find that Ajax requests will also get the extra information appended to the output.
In order to circumvent this add this to your &#8220;app_controller.php&#8221; file (which sits directly inside the &#8220;app/&#8221; [...]]]></description>
			<content:encoded><![CDATA[<p>If you are developing a <a href="http://cakephp.org">CakePHP</a> application and you have the debug value set to anything greater than 0 in your config/core.php file, you will find that Ajax requests will also get the extra information appended to the output.</p>
<p>In order to circumvent this add this to your &#8220;app_controller.php&#8221; file (which sits directly inside the &#8220;app/&#8221; dir, rather than in &#8220;app/controllers&#8221;):</p>
<pre class="brush: php">
&lt;?php
class AppController extends Controller {
  var $components = array(&#039;RequestHandler&#039;);
  var $helpers = array(&#039;Html&#039;,&#039;Form&#039;,&#039;Ajax&#039;);
  function beforeFilter() {
    if ( $this-&gt;RequestHandler-&gt;isAjax() ) {
      Configure::write(&#039;debug&#039;,0);
    }
  }
}
?&gt;
</pre>
<p>Note that while this will disable the debugging output, it will also have other affects too (for the life of the Ajax Request) like extending the length of time that the &#8220;schema&#8221; is cached.  This should make little or no difference, but is worth remembering.</p>
]]></content:encoded>
			<wfw:commentRss>http://otaqui.com/blog/430/disable-debug-output-for-ajax-in-cakephp/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Mozilla Fennec Alpha 2 Release</title>
		<link>http://otaqui.com/blog/428/mozilla-fennec-alpha-2-release/</link>
		<comments>http://otaqui.com/blog/428/mozilla-fennec-alpha-2-release/#comments</comments>
		<pubDate>Tue, 10 Feb 2009 20:29:29 +0000</pubDate>
		<dc:creator>pete</dc:creator>
				<category><![CDATA[Professional]]></category>
		<category><![CDATA[links]]></category>
		<category><![CDATA[mozilla]]></category>
		<category><![CDATA[software]]></category>
		<category><![CDATA[webdev]]></category>

		<guid isPermaLink="false">http://otaqui.com/blog/428/mozilla-fennec-alpha-2-release/</guid>
		<description><![CDATA[Mozilla has released the second alpha of Fennec, their mobile browser.  There&#8217;s a short but sweet overview of Fennec on Vimeo which is worth checking out.
I think almost regardless of it&#8217;s performance improvements the real game-changing potential here is the inevitable swathe of add-ons and extensions that will be, and in some cases such [...]]]></description>
			<content:encoded><![CDATA[<p>Mozilla has <a href="http://blog.mozilla.com/blog/2008/12/23/fennec-alpha-2-released/">released the second alpha of Fennec</a>, their mobile browser.  There&#8217;s a short but sweet <a href="http://www.vimeo.com/2577978">overview of Fennec on Vimeo</a> which is worth checking out.</p>
<p>I think almost regardless of it&#8217;s <a href="http://starkravingfinkle.org/blog/2008/12/fennec-alpha2-performance/">performance improvements</a> the real game-changing potential here is the inevitable swathe of add-ons and extensions that will be, and in some cases such as <a href="http://hackademix.net/2008/12/23/noscript-for-fennec-update/">NoScript</a> already are, available.</p>
<p>I don&#8217;t think that any web developer who has used it would want to go back to a world without <a href="http://getfirebug.com/">Firebug</a> and I also think that it&#8217;s influence on <a href="http://webkit.org">WebKit</a>&#8217;s developer tools is fairly clear to see.</p>
<p>So I really hope that support for Fennec spreads beyond the Nokia 810 soon, and that it&#8217;s as instrumental as Firefox was in re-igniting browser development, especially from a developer-tool perspective.</p>
]]></content:encoded>
			<wfw:commentRss>http://otaqui.com/blog/428/mozilla-fennec-alpha-2-release/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Page Optimisation Technique &#8211; Build Two Rows For The Fold</title>
		<link>http://otaqui.com/blog/415/page-optimisation-technique-build-two-rows-for-the-fold/</link>
		<comments>http://otaqui.com/blog/415/page-optimisation-technique-build-two-rows-for-the-fold/#comments</comments>
		<pubDate>Sat, 07 Feb 2009 14:01:07 +0000</pubDate>
		<dc:creator>pete</dc:creator>
				<category><![CDATA[Professional]]></category>
		<category><![CDATA[page-optimisation]]></category>
		<category><![CDATA[webdev]]></category>

		<guid isPermaLink="false">http://otaqui.com/blog/?p=415</guid>
		<description><![CDATA[Despite suggestions to the contrary, we web developers still very much have to work with the concept of a &#8220;fold&#8221; &#8211; that is the part of the page that is immediately visible when a user visits your site.
This is usually considered to mean &#8220;before the user has to scroll down&#8221; but can also just mean [...]]]></description>
			<content:encoded><![CDATA[<p>Despite suggestions to the contrary, we web developers still very much have to work with the concept of a &#8220;fold&#8221; &#8211; that is the part of the page that is immediately visible when a user visits your site.</p>
<p>This is usually considered to mean &#8220;before the user has to scroll down&#8221; but can also just mean &#8220;the stuff they look at first&#8221; even if they have a really big monitor.  This gives us a slightly more useful thing to think about &#8211; the first &#8220;batch&#8221; of content that will be at the top of the page, and incidentally will be the entirety of what a considerable portion of our users see first.</p>
<p>So, I have a suggestion to all the web *designers* out there to mull over: introduce the idea of &#8220;Two Rows&#8221; in your design.  By having a single row that is for content &#8220;above the fold&#8221; and a second one for everything below, we can get the absolute minimum &#8220;perceived load time&#8221; of the page.</p>
<p>Let&#8217;s have a look at a fairly standard wireframe of a page:</p>
<div id="attachment_416" class="wp-caption aligncenter" style="width: 430px"><a href="http://otaqui.com/blog/wp-content/uploads/layout_normal.png"><img src="http://otaqui.com/blog/wp-content/uploads/layout_normal.png" alt="&quot;Normal&quot; three column page layout" title="Normal three column page layout" width="420" height="420" class="size-full wp-image-416" /></a><p class="wp-caption-text">Normal three column page layout</p></div>
<p>This will be more or less as you expect, a header and footer with 3 (or however many) columns in between.</p>
<p>Now let&#8217;s have a look at a wireframe designed for the fold:</p>
<div id="attachment_417" class="wp-caption aligncenter" style="width: 430px"><a href="http://otaqui.com/blog/wp-content/uploads/layout_optimised.png"><img src="http://otaqui.com/blog/wp-content/uploads/layout_optimised.png" alt="Optimised For the Fold Layout" title="Optimised For the Fold Layout" width="420" height="420" class="size-full wp-image-417" /></a><p class="wp-caption-text">Optimised For the Fold Layout</p></div>
<p>As you can see, I&#8217;ve changed the structure to split the main content completely into two distinct rows.</p>
<p>The advantage here is that we developers can do several things to make the first row appear as quickly as possible:</p>
<ol>
<li>Firstly we don&#8217;t have to do anything.  The source order itself will mean that the content of the first row is loaded first, meaning that you won&#8217;t have that all-to-common &#8220;pause for the right-hand-column&#8221; to load.</li>
<li>Place all styles at the top of the page, and all required scripts between the two rows &#8211; this will make the page appear correctly styled and have javascript-driven functionality available.</li>
<li>Taking the point above to a further degree, we could in fact *split* the javascript (and possibly even css) files, which is normally a abad idea, to provide only the functionality required for the &#8220;first row&#8221;, and then load other resources afterwards.  This is a rather extreme approach, but could help if your site really needs the content &#8220;above the fold&#8221; to be absolutely as fast as possible even at the cost of overall page performance.</li>
<li>With a dynamic site, we can flush the buffer after the first row, and leave any further processing overhead to happen after that much of the page has been sent to the browser.</li>
</ol>
<p>There is no real need for the actual design to exagerate the rows in the same way that the wireframe does &#8211; but you would need a horizontal split of the content at that point to make things as easy and flexible as possible.  This limitation can be overcome with CSS, and possibly javascript, but this will rapidly become fiddly and hard to maintain.</p>
<p>To demonstrate what I mean, I took the Guardian&#8217;s homepage, and reworked it (as a graphic) to have the horizontal split required for Two Rows, without really appearing much different to the current, pure-column structure.</p>
<p>Before:<br />
<div id="attachment_423" class="wp-caption aligncenter" style="width: 310px"><a href="http://otaqui.com/blog/wp-content/uploads/guardian_original.jpg"><img src="http://otaqui.com/blog/wp-content/uploads/guardian_original-300x231.jpg" alt="Original Guardian Homepage" title="Guardian Original" width="300" height="231" class="size-medium wp-image-423" /></a><p class="wp-caption-text">Original Guardian Homepage</p></div></p>
<p>And after (with the &#8220;second row&#8221; being slightly greyed out):<br />
<div id="attachment_422" class="wp-caption aligncenter" style="width: 310px"><a href="http://otaqui.com/blog/wp-content/uploads/guardian_modified.jpg"><img src="http://otaqui.com/blog/wp-content/uploads/guardian_modified-300x231.jpg" alt="Modified Guardian Page" title="Guardian Modified" width="300" height="231" class="size-medium wp-image-422" /></a><p class="wp-caption-text">Modified Guardian Page</p></div></p>
<p>This example worked fairly well in terms of &#8220;hiding&#8221; the row lines since some things are bumped right up to them and others have varying amounts of whitespace either side.</p>
<p>I must admit that I&#8217;ve never implemented this technique, but I certainly believe that it could have significant impact for a site like the Guardian that has a large amount of content &#8220;below the fold&#8221;.</p>
]]></content:encoded>
			<wfw:commentRss>http://otaqui.com/blog/415/page-optimisation-technique-build-two-rows-for-the-fold/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Custom Model and Field Result Set in CakePHP</title>
		<link>http://otaqui.com/blog/402/custom-model-and-field-result-set-in-cakephp/</link>
		<comments>http://otaqui.com/blog/402/custom-model-and-field-result-set-in-cakephp/#comments</comments>
		<pubDate>Thu, 05 Feb 2009 17:22:44 +0000</pubDate>
		<dc:creator>pete</dc:creator>
				<category><![CDATA[Professional]]></category>
		<category><![CDATA[cakephp]]></category>
		<category><![CDATA[webdev]]></category>

		<guid isPermaLink="false">http://otaqui.com/blog/?p=402</guid>
		<description><![CDATA[If you&#8217;re using CakePHP and want to perform a custom SQL query while forcing your results into an arbitrary Model-based array when they are given back to you, you could do a lot worse than using grigri&#8217;s (who is a fellow South Westerner and therefore obviously a good bloke ;) DboMysqlEx (Mysql Extended) Class, or [...]]]></description>
			<content:encoded><![CDATA[<p>If you&#8217;re using CakePHP and want to perform a custom SQL query while forcing your results into an arbitrary Model-based array when they are given back to you, you could do a lot worse than using grigri&#8217;s (who is a fellow South Westerner and therefore obviously a good bloke ;) DboMysqlEx (Mysql Extended) Class, or at least this particular part of it:</p>
<pre class="brush: php">&lt;?php
require_once (LIBS . &#039;model&#039; . DS . &#039;datasources&#039; . DS . &#039;dbo&#039; . DS . &#039;dbo_mysql.php&#039;);
class DboMysqlEx extends DboMysql {
   var $description = &quot;MySQL DBO Driver - Extended&quot;;
   // Override resultSet to allow for Model__field type aliases
   function resultSet(&amp;$results) {
		if (isset($this-&gt;results) &amp;&amp; is_resource($this-&gt;results) &amp;&amp; $this-&gt;results != $results) {
			mysql_free_result($this-&gt;results);
		}
       $this-&gt;results =&amp; $results;
       $this-&gt;map = array();
       $num_fields = mysql_num_fields($results);
       $index = 0;
       $j = 0;
       while ($j &lt; $num_fields) {
           $column = mysql_fetch_field($results,$j);
           if (!empty($column-&gt;table)) {
               $this-&gt;map[$index++] = array($column-&gt;table, $column-&gt;name);
           } else {
               if (strpos($column-&gt;name, &#039;__&#039;)) {
                   $parts = explode(&#039;__&#039;, $column-&gt;name);
                   $this-&gt;map[$index++] = array($parts[0], $parts[1]);
               } else {
                   $this-&gt;map[$index++] = array(0, $column-&gt;name);
               }
           }
           $j++;
       }
   }
}
?&gt;
</pre>
<p>This code, which overrides the default MySQL DBO, allows you to SELECT something AS Modelname__Fieldname (with a double underscore) and will end up populating your result set as you would like it to ($results['Modelname']['Fieldname']).</p>
<p>In order to use a custom datasource, copy the code and save it as &#8220;/cake_dir/app/models/datasources/dbo/dbo_mysqlex.php&#8221; and edit your &#8220;/cake_dir/app/config/database.php&#8221; file so that it uses the &#8220;mysqlex&#8221; driver instead of plain old &#8220;mysql&#8221;.</p>
<p>This all came from a discussion on the excellent <a href="http://groups.google.com/group/cake-php/browse_thread/thread/f823cc7f168cd619?hl=en">CakePHP Google Group</a></p>
<p>Grigri posted <a href="http://bin.cakephp.org/view/1781729779">a copy of his modified MySQL Extended DBO</a> online if you want some more of his nice features like backtracing.</p>
]]></content:encoded>
			<wfw:commentRss>http://otaqui.com/blog/402/custom-model-and-field-result-set-in-cakephp/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Selecting a Different Table Column if the Original Record is NULL in MYSQL Using IFNULL</title>
		<link>http://otaqui.com/blog/398/selecting-a-different-table-column-if-the-original-record-is-null-in-mysql-using-ifnull/</link>
		<comments>http://otaqui.com/blog/398/selecting-a-different-table-column-if-the-original-record-is-null-in-mysql-using-ifnull/#comments</comments>
		<pubDate>Thu, 05 Feb 2009 13:31:13 +0000</pubDate>
		<dc:creator>pete</dc:creator>
				<category><![CDATA[Professional]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[webdev]]></category>

		<guid isPermaLink="false">http://otaqui.com/blog/?p=398</guid>
		<description><![CDATA[MySQL&#8217;s documentation is OK, but it&#8217;s examples are sometimes quite poor.
I have a particular setup where values across two tables &#8211; a course table and a schedule table which relates to it &#8211; can be effectively &#8220;overridden&#8221;.  The idea is that for any given course, one can set &#8220;default&#8221; values, and then these can [...]]]></description>
			<content:encoded><![CDATA[<p>MySQL&#8217;s documentation is OK, but it&#8217;s examples are sometimes quite poor.</p>
<p>I have a particular setup where values across two tables &#8211; a course table and a schedule table which relates to it &#8211; can be effectively &#8220;overridden&#8221;.  The idea is that for any given course, one can set &#8220;default&#8221; values, and then these can be overridden every time a course is actually scheduled to happen.</p>
<p>The courses table looks something like this:</p>
<p>COURSES<br />
id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY<br />
title VARCHAR(255) NOT NULL<br />
subtitle VARCHAR(255) NOT NULL<br />
description TEXT NOT NULL</p>
<p>SCHEDULES<br />
id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY<br />
course_id INT(11) NOT NULL<br />
subtitle VARCHAR(255) NULL<br />
description TEXT NULL</p>
<p>Hopefully you can see what I would want during a SELECT &#8211; to get the values from the schedules table if they aren&#8217;t null, or otherwise get the value from the courses table if they are.</p>
<p>This is achieved with the IFNULL built in function in MySQL.  IFNULL takes two arguments, and returns the first argument if it (the first argument that is) is not null, or the second argument if it is.  The actual SQL for my example looks like this:</p>
<p>SELECT courses.title, IFNULL(schedules.subtitle,courses.subtitle) AS subtitle, IFNULL(schedules.description,courses.description) AS description FROM schedules LEFT JOIN courses ON schedule.course_id=course.id;</p>
<p>This will give you the &#8220;subtitle&#8221; and &#8220;description&#8221; values from the linked table (schedules) if they exist, or the default values from the original table (courses) if they don&#8217;t.</p>
]]></content:encoded>
			<wfw:commentRss>http://otaqui.com/blog/398/selecting-a-different-table-column-if-the-original-record-is-null-in-mysql-using-ifnull/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Performing Operations on MySQL Data While Copying between Tables</title>
		<link>http://otaqui.com/blog/396/performing-operations-on-mysql-data-while-copying-between-tables/</link>
		<comments>http://otaqui.com/blog/396/performing-operations-on-mysql-data-while-copying-between-tables/#comments</comments>
		<pubDate>Sun, 01 Feb 2009 17:11:06 +0000</pubDate>
		<dc:creator>pete</dc:creator>
				<category><![CDATA[Professional]]></category>
		<category><![CDATA[mysql]]></category>

		<guid isPermaLink="false">http://otaqui.com/blog/?p=396</guid>
		<description><![CDATA[I had the need to copy a load of data from one table to another while incrementing (or just adding an arbitrary number to) some numeric indices.
Before I lept to a programming language like PHP to do a load of selects, manipulate the data and then run inserts I thought I&#8217;d try and do it [...]]]></description>
			<content:encoded><![CDATA[<p>I had the need to copy a load of data from one table to another while incrementing (or just adding an arbitrary number to) some numeric indices.</p>
<p>Before I lept to a programming language like PHP to do a load of selects, manipulate the data and then run inserts I thought I&#8217;d try and do it in pure SQL.  It turns out this is fairly straightforward.</p>
<p>Let&#8217;s assume we have two databases, db_source and db_target and that we want to copy stuff from the former into the latter.  Both of these have identical tables called &#8216;users&#8217; and &#8216;posts&#8217; like so:</p>
<p>USERS<br />
id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,<br />
name VARCHAR(255) NOT NULL</p>
<p>POSTS<br />
id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,<br />
user_id INT(11) NOT NULL,<br />
title VARCHAR(255),<br />
post TEXT</p>
<p>Now, let&#8217;s assume that in db_target we *already* have some posts and some users, and what we really want to do is merge the records from db_source with the ones that already in db_target, obviously maintaining all the related keys.</p>
<p>In this example we will say that we have 5 current users in db_target, and 100 posts; and also that the auto_increment values are 1 to 1 in both tables (that is the next auto_increment value will be 6 and 101 for users and posts respectively in db_target).</p>
<p>With these two commands, which will run SO much faster than some kind of programmatic loop, we can copy all the users and posts at a stroke without losing any associations:</p>
<p># copy the users, adding 5 to the user id while we&#8217;re at it:<br />
INSERT INTO db_target.users ( id, name ) SELECT (id+5) AS id, name FROM db_source.users;</p>
<p># copy the posts, adding 5 to the user_id and 100 to the post id:<br />
INSERT INTO db_target.posts ( id, user_id, title, post ) SELECT (id+100) AS id, (user_id+5) AS user_id, title, post FROM db_source.posts;</p>
<p>Note that I have selected &#8220;as&#8221; where I have been using the addition operator. Given that we are also specifying which columns we are updating I&#8217;m not 100% sure that it&#8217;s necessary, but I find it helpful anyway.</p>
]]></content:encoded>
			<wfw:commentRss>http://otaqui.com/blog/396/performing-operations-on-mysql-data-while-copying-between-tables/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Enable Google Gears and Offline Gmail for Mozilla Prism</title>
		<link>http://otaqui.com/blog/382/enable-google-gears-and-offline-gmail-for-mozilla-prism/</link>
		<comments>http://otaqui.com/blog/382/enable-google-gears-and-offline-gmail-for-mozilla-prism/#comments</comments>
		<pubDate>Fri, 30 Jan 2009 10:18:01 +0000</pubDate>
		<dc:creator>pete</dc:creator>
				<category><![CDATA[Professional]]></category>
		<category><![CDATA[browser]]></category>
		<category><![CDATA[downloads]]></category>
		<category><![CDATA[mac]]></category>
		<category><![CDATA[software]]></category>

		<guid isPermaLink="false">http://otaqui.com/blog/?p=382</guid>
		<description><![CDATA[[Warning: this post suggests stuff that may not work now, or may break in the future.  Use at your own risk!]
I&#8217;m really happy to hear that, as the official google blog points out, Gmail now has support for Google Gears allowing you to view your mail offline.
This has caused quite a stir in the [...]]]></description>
			<content:encoded><![CDATA[<p><em>[<strong>Warning</strong>: this post suggests stuff that may not work now, or may break in the future.  Use at your own risk!]</em></p>
<p>I&#8217;m really happy to hear that, <a href="http://gmailblog.blogspot.com/2009/01/new-in-labs-offline-gmail.html">as the official google blog points out</a>, Gmail now has support for Google Gears allowing you to view your mail offline.</p>
<p>This has caused quite a stir in the blogging community &#8211; <a href="http://www.techcrunch.com/2009/01/27/gmail-goes-offline-with-google-gears/">TechCrunch invites you to throw off your chains</a>, <a href="http://googlewatch.eweek.com/content/google_gears/googles_offline_gmail_points_to_greater_google_gears_exposure.html">GoogleWatch speculates that it might be the tipping point for Gears adoption</a>, <a href="http://www.bytebot.net/blog/archives/2009/01/28/offline-gmail-via-google-gears">Colin Charles mentioning SSBs such as Fluid and Prism</a> (which this article is about), <a href="http://googlesystem.blogspot.com/2009/01/10-good-things-about-offline-gmail.html">Google Operating System giving a basic overview</a> and also <a href="http://gadgetwise.blogs.nytimes.com/2009/01/29/googles-gmail-now-works-offline/">the GadgetWise NYT blog giving it a spread</a>.</p>
<p>So far, so good.  Now what if we want to use a Site Specific Browser, such as those created by <a href="http://fluidapp.com">Fluid</a> and <a href="http://labs.mozilla.com/projects/prism/">Mozilla Prism</a>?  These &#8220;SSBs&#8221; are essentially entire browsers, but used only for a single web app &#8211; which is quite handy really.  I think that Google&#8217;s paradigm of basically making each tab / window in your browser it&#8217;s own application instance may well remove the need for things like this eventually, but for the time being it would be great to use Gmail&#8217;s new offline capability in an SSB.</p>
<p>Well &#8211; we can&#8217;t do much about enabling Gears in Fluid, since it&#8217;s based on WebKit.  Check back if you here that Google releases some frm of plugin for WebKit (although actually WK already has HTML5 style &#8220;local storage&#8221; using SQLite so it could be native support).</p>
<p>We can however do some hacking to get Gears installed in a Prism browser, so let&#8217;s go through the process.  By the way &#8211; I am using Mac OS X 10.5, but I think this should work for any platform that can get Firefox and Google Gears.</p>
<p>I&#8217;m using a Google Apps For Your Domain account, it&#8217;s worked fine there, although your account will have to have Offline access enabled by google (patience, young grasshopper!).</p>
<h3>Step 1: Split a Beam</h3>
<p>Go and <a href="http://labs.mozilla.com/projects/prism/">install Mozilla Prism</a>. I did this by installing it as an extension to my current Firefox.</p>
<p>Once it&#8217;s installed and you have restarted the browser, login to your Gmail and from the Tools menu choose &#8220;Convert Website To Application.&#8221;</p>
<p>Choose whatever options you like in terms of where to put the shortcut.  Personally I grabbed <a href="http://chris.ivarson.name/goodies/">Chris Ivarson&#8217;s awesome Fluid Gmail Icons</a> and used Preview.app to convert the 128&#215;128 version into a PNG file.</p>
<p>I *also* had a slight issue with not being able to create the app inside the Applications folder, because it needed to Authenticate but just failed silently &#8211; YMMV.</p>
<p>Assuming that&#8217;s done you should have a nice new Gmail SSB, which you can start and log in to Gmail with.  Funky.</p>
<h3>Step 2: Start The Fans, Please!</h3>
<p>[Note: you have to be older than young, from the UK and to know who Richard O'Brien is to get that "start the fans" bit]</p>
<p>So now we need to get Google Gears installed.  You can try doing this from Gmail&#8217;s own interface but it will fail complaining that &#8220;Gears is not compatiable with XYZ&#8221; (where XYZ is the name you gave it and a version number).  This is because, even though your Gmail webapp *is( Firefox under the hood, it doesn&#8217;t report itself as such to the extension.  What we need to do is convince the extension that it is in fact compatible with it.</p>
<p>[Note: it might be more efficient if you want to install several extensions to make your SSB report itself as really being Firefox, but I'm happy with a one-at-a-time approach].</p>
<p>The next thing to do is actually download Google Gears manually.  I suggest that you really do try (and fail) to install it from inside the Prism app, because you will be told the current, valid URL for the XPI in the process.  If you&#8217;re super lazy, here&#8217;s where I got mine from (not a link so that you&#8217;re encouraged to FIND THE RIGHT URL FOR YOURSELF!):</p>
<p>http://dl.google.com/gears/current/gears-osx-opt.xpi</p>
<p>You might want to use Safari / Opera / wget / IE to actually download it.</p>
<h3>Step 3: Unpack, Tweak, Repack</h3>
<p>An XPI file is in fact just a ZIP file with delusions of grandeur.  Rename the file to &#8220;gears-osx-opt.zip&#8221; and unpack it.</p>
<p>Now open up &#8220;install.rdf&#8221; and find the bit that looks like this:</p>
<p>&lt;em:targetApplication&gt;<br />
&lt;Description&gt;<br />
&lt;em:id&gt;{ec8030f7-c20a-464f-9b0e-13a3a9e97384}&lt;/em:id&gt;<br />
&lt;em:minVersion&gt;1.5&lt;/em:minVersion&gt;<br />
&lt;em:maxVersion&gt;3.0.*&lt;/em:maxVersion&gt;<br />
&lt;/Description&gt;<br />
&lt;/em:targetApplication&gt;</p>
<p>The big long string in curly brackets is the Firefox GUID.  We will need to change this, and also the version numbers.</p>
<p>I found the GUID and version numbers inside my (OS X-specific) &#8220;Gmail.app&#8221; that Prism created.  Control+Click on it and choose &#8220;Show Package Contents&#8221; then navigate to &#8220;Contents/Resources/application.ini&#8221; and open this in a text editor.  It is the &#8220;Version&#8221; and &#8220;ID&#8221; values that you want from the &#8220;[App]&#8221; section.</p>
<p>Applying these to the original install.rdf file we get this (I&#8217;ve extended the version numbers just for the hell of it):<br />
&lt;em:targetApplication&gt;<br />
&lt;Description&gt;<br />
&lt;em:id&gt;prism@developer.mozilla.org&lt;/em:id&gt;<br />
&lt;em:minVersion&gt;0.1.*&lt;/em:minVersion&gt;<br />
&lt;em:maxVersion&gt;10.0.*&lt;/em:maxVersion&gt;<br />
&lt;/Description&gt;<br />
&lt;/em:targetApplication&gt;</p>
<p>Save the edited &#8220;install.rdf&#8221; file and now you need to repack the zip / xpi.  Take note &#8211; you should compress the *files* and not the *directory* or it won&#8217;t work.</p>
<p>Change your newly created zip file&#8217;s extension back to XPI &#8211; and we&#8217;re ready to install!</p>
<h3>Step 4: Install</h3>
<p>This may vary across platforms, I&#8217;m not sure.</p>
<p>From the *status bar* (an unusual place to get to the Add Ons / Extensions list) of your Prism app, click the cog and choose &#8220;Tools&#8221;, &#8220;Addons&#8221;.  This should pop open the Add Ons panel.</p>
<p>Drag and drop the XPI onto the Add Ons panel and, hopefully, bingo!</p>
<p>If you get an error about &#8220;version support&#8221; then you might have got the ID or version number wrong somewhere.  If you get one about &#8220;cannot find install script&#8221; then your XPI / zip is badly formed.  Leave a comment if you need more help here.</p>
<h3>Step 5: Enable Gears for Gmail</h3>
<p>You&#8217;ll need to restart your browser and log back in to GMail.  Then you should be able to click the &#8220;Offline&#8221; icon at the top of the interface and go through the normal Gears &#8220;I trust this application&#8221; process.</p>
<p>That&#8217;s it!  You should now be able to access your mail offline.  It&#8217;s worth noting that the shortcut that gears can create for you to <em>open</em> your email while offline seems to work fine too.</p>
<p><em>[Update 31st Jan 2009: It looks like <a href="http://fluidapp.com/blog/2008/05/28/fluid-gears-update/">Fluid may support Gears sometime soon</a>!]</em></p>
]]></content:encoded>
			<wfw:commentRss>http://otaqui.com/blog/382/enable-google-gears-and-offline-gmail-for-mozilla-prism/feed/</wfw:commentRss>
		<slash:comments>16</slash:comments>
		</item>
	</channel>
</rss>
