<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <title>yob.id.au</title>
  <link>http://yob.id.au/</link>

  <item>
    <title>Flash 10 On 64bit Debian</title>
    <link>http://yob.id.au/2010/05/22/flash-10-on-64bit-debian.html</link>
    <guid>http://yob.id.au/2010/05/22/flash-10-on-64bit-debian</guid>
    <updated>2010-05-22T00:00:00+10:00</updated>
    <description>&lt;p&gt;Debian recently removed flash from their AMD64 to security issues and Adobe dropping official support.&lt;/p&gt;

&lt;p&gt;I dislike flash as much as the next self respecting web developer, but some site (like google analytics) are pretty useless without it.&lt;/p&gt;

&lt;p&gt;If you need a temporary solution to getting it up and running, manually download and install the old flash9 package from backports&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;    wget http://backports.mithril-linux.org/pool/contrib/f/flashplugin-nonfree/flashplugin-nonfree_1.4~bpo40+1_amd64.deb
    sudo dpkg -i flashplugin-nonfree_1.4~bpo40+1_amd64.deb
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;For further reading, check out the backports page for &lt;a href='http://packages.debian.org/etch-backports/flashplugin-nonfree'&gt;flashplugin-nonfree&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It&amp;#8217;s ye olde, but it works.&lt;/p&gt;</description>
  </item>

  <item>
    <title>New Mysql Ruby Bindings</title>
    <link>http://yob.id.au/2010/05/20/new-mysql-ruby-bindings.html</link>
    <guid>http://yob.id.au/2010/05/20/new-mysql-ruby-bindings</guid>
    <updated>2010-05-20T00:00:00+10:00</updated>
    <description>&lt;p&gt;There&amp;#8217;s been some recent discussions about the way ruby 1.9 handles encodings and the exceptions people are running into, particularly when running rails.&lt;/p&gt;

&lt;p&gt;A lot of the time, the exceptions are caused by database drivers passing back multi-byte strings incorrectly tagged as ASCII-8BIT instead of UTF-8.&lt;/p&gt;

&lt;p&gt;The work around was ususally to do something like the monkey patch described &lt;a href='http://gnuu.org/2009/11/06/ruby19-rails-mysql-utf8/'&gt;here&lt;/a&gt;. I&amp;#8217;ve used that particular patch in production for a few months and it works fine, but monkey patches make me squirm.&lt;/p&gt;

&lt;p&gt;This week I was finally able to replace the patch with an alternative mysql gem that retrieves all strings from the database as utf-8 and marks them correctly.&lt;/p&gt;

&lt;p&gt;Brian Mario has released &lt;a href='http://github.com/brianmario/mysql2'&gt;mysql2&lt;/a&gt;. The basic API is different to the standard mysql gem, but he&amp;#8217;s included an ActiveRecord adapter that acts as a drop-in replacement for the mysql one.&lt;/p&gt;

&lt;p&gt;To give mysql2 a go in rails install the gem:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;    gem install mysql2
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Then update your database.yml:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='yaml'&gt;    &lt;span class='l-Scalar-Plain'&gt;development&lt;/span&gt;&lt;span class='p-Indicator'&gt;:&lt;/span&gt;
      &lt;span class='l-Scalar-Plain'&gt;adapter&lt;/span&gt;&lt;span class='p-Indicator'&gt;:&lt;/span&gt; &lt;span class='l-Scalar-Plain'&gt;mysql2&lt;/span&gt;
      &lt;span class='l-Scalar-Plain'&gt;database&lt;/span&gt;&lt;span class='p-Indicator'&gt;:&lt;/span&gt; &lt;span class='l-Scalar-Plain'&gt;myapp_dev&lt;/span&gt;
      &lt;span class='l-Scalar-Plain'&gt;username&lt;/span&gt;&lt;span class='p-Indicator'&gt;:&lt;/span&gt; &lt;span class='l-Scalar-Plain'&gt;foo&lt;/span&gt;
      &lt;span class='l-Scalar-Plain'&gt;password&lt;/span&gt;&lt;span class='p-Indicator'&gt;:&lt;/span&gt; &lt;span class='l-Scalar-Plain'&gt;bar&lt;/span&gt;
      &lt;span class='l-Scalar-Plain'&gt;host&lt;/span&gt;&lt;span class='p-Indicator'&gt;:&lt;/span&gt; &lt;span class='l-Scalar-Plain'&gt;localhost&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Make sure you get mysql2 version 0.1.6 or higher, in lower versions the ActiveRecord adaptor behaved slightly differently from the default Mysql one.&lt;/p&gt;

&lt;p&gt;Brian is incredibly responsive to bug reports, so if you have any issues make sure you report them on the github project.&lt;/p&gt;</description>
  </item>

  <item>
    <title>Ethical Superannuation</title>
    <link>http://yob.id.au/2010/01/16/ethical-superannuation.html</link>
    <guid>http://yob.id.au/2010/01/16/ethical-superannuation</guid>
    <updated>2010-01-16T00:00:00+11:00</updated>
    <description>&lt;p&gt;For all of my (admittedly short) professional life, my superannuation has been in &lt;a href='http://australiansuper.com/'&gt;Australian Super&amp;#8217;s&lt;/a&gt; Sustainable Balanced fund.&lt;/p&gt;

&lt;p&gt;I was more or less happy with this. Conventional wisdom says that non-profit industry super funds tend to perform better than commercial funds due to lower fees, and the sustainable option meant my money wasn&amp;#8217;t being invested in too many companies I disagreed with.&lt;/p&gt;

&lt;p&gt;However, I recently received a letter from Australian Super informing me of an upcoming change in their sustainable investment policies. The key paragraph was:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;In the past our investment manager excluded companies that it believed to
receive a significant proportion of their revenue (at least 5%) from the
manufacture of sale of alcohol or tobacco, the operation of gaming
facilities or the manufacture of gambling equipment, uranium extraction or
the manufacture of weapons or armaments. This automatic exclusion will no
longer take place.&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Hah. Imagine that. Presumably they were sick of missing investment returns from companies like BHP that are involved in uranium.&lt;/p&gt;

&lt;p&gt;Whatever their reasoning, chances are my money is now being invested in places I&amp;#8217;d prefer it not to be, so I&amp;#8217;ve been researching my options.&lt;/p&gt;

&lt;p&gt;The obvious alternative is &lt;a href='http://www.australianethical.com.au'&gt;Australian Ethical Super&lt;/a&gt;. Their investment policies gel strongly with my preferences and my non-super investments with them have been performing well.&lt;/p&gt;

&lt;p&gt;Being a commercial fund, a comparison of fees was in order. The results were surprising and a little disappointing.&lt;/p&gt;

&lt;p&gt;&lt;a href='http://australiansuper.com/formembers_feesandothercharges.aspx'&gt;Australian Super&amp;#8217;s Fees&lt;/a&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$78/year administration costs
0.77% of account balance
0% of contributions&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;a href='http://www.australianethical.com.au/fees-and-costs-individuals'&gt;Australian Ethical Fees&lt;/a&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$41/year administration costs
2.15% of account balance, calculated weekly
4.1% of contributions&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;For the 08-09 financial year, Australian Ethical fees for my super balance would&amp;#8217;ve been a little more than &lt;em&gt;triple&lt;/em&gt; what I actually paid Australian Super. Bugger.&lt;/p&gt;

&lt;p&gt;All this is ignoring historical returns for each fund, a topic that would demand an entirely separate blog post. The short of it is that over the previous 5 years, Australian Ethical seems to have performed worse on comparable funds.&lt;/p&gt;

&lt;p&gt;So do I move? Are there other ethical super options out there? What premium am I willing to cop in return for having my money invested in ethical companies?&lt;/p&gt;</description>
  </item>

  <item>
    <title>Intro To The Pdf File Structure</title>
    <link>http://yob.id.au/2009/11/12/intro-to-the-pdf-file-structure.html</link>
    <guid>http://yob.id.au/2009/11/12/intro-to-the-pdf-file-structure</guid>
    <updated>2009-11-12T00:00:00+11:00</updated>
    <description>&lt;p&gt;For a couple of years I&amp;#8217;ve been involved in various PDF related Ruby projects - most recently in &lt;a href='http://prawn.majesticseacreature.com/'&gt;prawn&lt;/a&gt;, a pure ruby PDF generation library.&lt;/p&gt;

&lt;p&gt;Prawn is a long way from implementing all parts of the spec and there has been no shortage of people who desire support for various features. Many of these users are the kind who will submit a patch to an open source project when they find a missing feature, yet this has only happened to prawn in a small number of cases.&lt;/p&gt;

&lt;p&gt;The PDF spec is epic (version 1.7 is over 1300 pages), yet the basic structure is less complicated than it first appears. Don&amp;#8217;t be put off! We&amp;#8217;d love more contributors to prawn, so this post is intended to be a basic introduction to reading a PDF file - the first step in understanding how prawn does the reverse process. I&amp;#8217;ve created a &lt;a href='/files/hexagon.pdf'&gt;simple PDF&lt;/a&gt; to use as an example.&lt;/p&gt;

&lt;p&gt;The best way to think of the file is a set of objects (strings, hashes, symbols, integers, arrays, etc) in an object tree that descends from a single &amp;#8220;root&amp;#8221; hash.&lt;/p&gt;

&lt;p&gt;To start navigating the &lt;a href='/files/hexagon.pdf'&gt;example file&lt;/a&gt; by eye, open it in a text editor and browse to the last line. Just above the end of the file token (%%EOF) is a number that indicates a byte offset in the file.&lt;/p&gt;

&lt;p&gt;That byte offset points to the &amp;#8220;xref&amp;#8221; token on line 46. Everything between here and the &amp;#8220;trailer&amp;#8221; token several lines below is called the Cross Reference Table (xref table to its friends). &amp;#8220;0 6&amp;#8221; indicates that this file has 5 objects in it numbered 1-5 (plus a number 0 null object). The following lines indicate object 1 is at byte offset 15, object 2 at offset 71, etc.&lt;/p&gt;

&lt;p&gt;After the byte offsets you&amp;#8217;ll see the following lines:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;    &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='sr'&gt;/Info 1 0 R&lt;/span&gt;
&lt;span class='sr'&gt;    /&lt;/span&gt;&lt;span class='no'&gt;Size&lt;/span&gt; &lt;span class='mi'&gt;6&lt;/span&gt;
    &lt;span class='o'&gt;/&lt;/span&gt;&lt;span class='no'&gt;Root&lt;/span&gt; &lt;span class='mi'&gt;3&lt;/span&gt; &lt;span class='mi'&gt;0&lt;/span&gt; &lt;span class='n'&gt;R&lt;/span&gt;
    &lt;span class='o'&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This is called the trailer and it is a serialised hashmap. In Ruby syntax, this would look something like:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;    &lt;span class='p'&gt;{&lt;/span&gt;
      &lt;span class='ss'&gt;:Info&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;1 0 R&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
      &lt;span class='ss'&gt;:Size&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='mi'&gt;6&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
      &lt;span class='ss'&gt;:Root&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;3 0 R&amp;quot;&lt;/span&gt;
    &lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This is the top of our object tree. &amp;#8220;1 0 R&amp;#8221; is the syntax for pointing to object #1. Using our xref table we can see object 1 is at byte 17 and it looks like this:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;    &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='sr'&gt;/Creator (Prawn)&lt;/span&gt;
&lt;span class='sr'&gt;    /&lt;/span&gt;&lt;span class='no'&gt;Producer&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;Prawn&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='o'&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;It&amp;#8217;s another hash that looks like this in Ruby:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;    &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='ss'&gt;:Creator&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Prawn&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:Producer&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Prawn&amp;quot;&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Merge this with our trailer hash and our object tree now looks like:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;    &lt;span class='p'&gt;{&lt;/span&gt;
      &lt;span class='ss'&gt;:Info&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='ss'&gt;:Creator&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Prawn&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:Producer&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Prawn&amp;quot;&lt;/span&gt; &lt;span class='p'&gt;},&lt;/span&gt;
      &lt;span class='ss'&gt;:Size&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='mi'&gt;6&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
      &lt;span class='ss'&gt;:Root&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;3 0 R&amp;quot;&lt;/span&gt;
    &lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Repeat this process for every time you see &amp;#8220;n 0 R&amp;#8221; (called an indirect object) and you will have a complete PDF object graph.&lt;/p&gt;

&lt;p&gt;Prawn&amp;#8217;s job is to do the reverse - build an object graph that conforms to the spec and serialise it to a file in the correct format. Prawn has all the tools for serialising standard ruby objects into their PDF representation, so the trick is to find the correct place to add your new information (an extra font, an advanced colour definition, whatever) and insert it as a ruby object. Usually this means something like adding an entry to an array or hash.&lt;/p&gt;

&lt;p&gt;Once that&amp;#8217;s done, prawn will handle the serialisation when a user calls render.&lt;/p&gt;

&lt;p&gt;For homework, download a copy of the &lt;a href='http://www.adobe.com/devnet/acrobat/pdfs/PDF32000_2008.pdf'&gt;PDF spec&lt;/a&gt; and read chapter 7 (particularly sections 7.3, 7.5, 7.7, 7.8 and 7.9). Also check out &lt;a href='http://github.com/sandal/prawn/blob/4b7f1a4d975b3478dad184bd56c7f53e7d92b784/lib/prawn/pdf_object.rb'&gt;lib/prawn/pdf_object.rb&lt;/a&gt; to see how prawn converts each Ruby object into PDF equivalents.&lt;/p&gt;</description>
  </item>

  <item>
    <title>Partition Resizing</title>
    <link>http://yob.id.au/2009/11/05/partition-resizing.html</link>
    <guid>http://yob.id.au/2009/11/05/partition-resizing</guid>
    <updated>2009-11-05T00:00:00+11:00</updated>
    <description>&lt;p&gt;This is for my own reference as much as anyone elses.&lt;/p&gt;

&lt;p&gt;For years I&amp;#8217;ve used an old copy of &lt;a href='http://www.symantec.com/norton/partitionmagic'&gt;Partition Magic&lt;/a&gt; when I needed to resize live partitions, usually as a step towards making a computer dual boot Windows and Linux.&lt;/p&gt;

&lt;p&gt;Last week I purchased a new laptop, only to find partition magic failed to even load. I suspect the 320Gb SATA drive had new geometry that freaked Partition Magic out with your standard cryptic error message (&amp;#8220;error 117&amp;#8221;).&lt;/p&gt;

&lt;p&gt;Not wanting to pay for a tool I use so irregularly, I hunted around for a free alternative and found one that worked nicely - &lt;a href='http://www.partition-tool.com/'&gt;Easeus Partition Master&lt;/a&gt;. The home edition is free for personal use.&lt;/p&gt;</description>
  </item>

  <item>
    <title>Dell E4300 And Linux</title>
    <link>http://yob.id.au/2009/11/05/dell-e4300-and-linux.html</link>
    <guid>http://yob.id.au/2009/11/05/dell-e4300-and-linux</guid>
    <updated>2009-11-05T00:00:00+11:00</updated>
    <description>&lt;p&gt;After 3.5 years of trusty service, it was time to retire my Dell Latitude D620. It was out of warranty, wouldn&amp;#8217;t charge batteries and with a NVidia graphics card it didn&amp;#8217;t play nice with Linux.&lt;/p&gt;

&lt;p&gt;After considering my options (mac? lenovo?) I decided to stick with Dell. I&amp;#8217;ve used their Latitude line for some time and enjoy the ruggedness, backed by the multi-year, next business day, onsite warranty. As a freelance web guy, it&amp;#8217;s hard to beat having a tech visit you with 24 hours to fix a broken machine.&lt;/p&gt;

&lt;p&gt;Wanting something slightly smaller than the D620, I settled on the &lt;a href='http://www1.ap.dell.com/au/en/business/notebooks/laptop_latitude_e4300/pd.aspx?refid=laptop_latitude_e4300&amp;amp;s=bsd&amp;amp;cs=aubsd1'&gt;Latitude E4300&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I&amp;#8217;m a Debian guy, and had no trouble installing the testing distrubution from the netinstall CD, then dist-upgrading to unstable.&lt;/p&gt;

&lt;p&gt;Hardware compatability is excellent - here&amp;#8217;s what lspci -nn tells me:&lt;/p&gt;

&lt;p&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sh'&gt;    &lt;span class='o'&gt;[&lt;/span&gt;jh@gaz Desktop&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='nv'&gt;$ &lt;/span&gt;lspci -nn
    00:00.0 Host bridge &lt;span class='o'&gt;[&lt;/span&gt;0600&lt;span class='o'&gt;]&lt;/span&gt;: Intel Corporation Mobile 4 Series Chipset Memory Controller Hub &lt;span class='o'&gt;[&lt;/span&gt;8086:2a40&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;(&lt;/span&gt;rev 07&lt;span class='o'&gt;)&lt;/span&gt;
    00:02.0 VGA compatible controller &lt;span class='o'&gt;[&lt;/span&gt;0300&lt;span class='o'&gt;]&lt;/span&gt;: Intel Corporation Mobile 4 Series Chipset Integrated Graphics Controller &lt;span class='o'&gt;[&lt;/span&gt;8086:2a42&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;(&lt;/span&gt;rev 07&lt;span class='o'&gt;)&lt;/span&gt;
    00:02.1 Display controller &lt;span class='o'&gt;[&lt;/span&gt;0380&lt;span class='o'&gt;]&lt;/span&gt;: Intel Corporation Mobile 4 Series Chipset Integrated Graphics Controller &lt;span class='o'&gt;[&lt;/span&gt;8086:2a43&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;(&lt;/span&gt;rev 07&lt;span class='o'&gt;)&lt;/span&gt;
    00:19.0 Ethernet controller &lt;span class='o'&gt;[&lt;/span&gt;0200&lt;span class='o'&gt;]&lt;/span&gt;: Intel Corporation 82567LM Gigabit Network Connection &lt;span class='o'&gt;[&lt;/span&gt;8086:10f5&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;(&lt;/span&gt;rev 03&lt;span class='o'&gt;)&lt;/span&gt;
    00:1a.0 USB Controller &lt;span class='o'&gt;[&lt;/span&gt;0c03&lt;span class='o'&gt;]&lt;/span&gt;: Intel Corporation 82801I &lt;span class='o'&gt;(&lt;/span&gt;ICH9 Family&lt;span class='o'&gt;)&lt;/span&gt; USB UHCI Controller &lt;span class='c'&gt;#4 [8086:2937] (rev 03)&lt;/span&gt;
    00:1a.1 USB Controller &lt;span class='o'&gt;[&lt;/span&gt;0c03&lt;span class='o'&gt;]&lt;/span&gt;: Intel Corporation 82801I &lt;span class='o'&gt;(&lt;/span&gt;ICH9 Family&lt;span class='o'&gt;)&lt;/span&gt; USB UHCI Controller &lt;span class='c'&gt;#5 [8086:2938] (rev 03)&lt;/span&gt;
    00:1a.2 USB Controller &lt;span class='o'&gt;[&lt;/span&gt;0c03&lt;span class='o'&gt;]&lt;/span&gt;: Intel Corporation 82801I &lt;span class='o'&gt;(&lt;/span&gt;ICH9 Family&lt;span class='o'&gt;)&lt;/span&gt; USB UHCI Controller &lt;span class='c'&gt;#6 [8086:2939] (rev 03)&lt;/span&gt;
    00:1a.7 USB Controller &lt;span class='o'&gt;[&lt;/span&gt;0c03&lt;span class='o'&gt;]&lt;/span&gt;: Intel Corporation 82801I &lt;span class='o'&gt;(&lt;/span&gt;ICH9 Family&lt;span class='o'&gt;)&lt;/span&gt; USB2 EHCI Controller &lt;span class='c'&gt;#2 [8086:293c] (rev 03)&lt;/span&gt;
    00:1b.0 Audio device &lt;span class='o'&gt;[&lt;/span&gt;0403&lt;span class='o'&gt;]&lt;/span&gt;: Intel Corporation 82801I &lt;span class='o'&gt;(&lt;/span&gt;ICH9 Family&lt;span class='o'&gt;)&lt;/span&gt; HD Audio Controller &lt;span class='o'&gt;[&lt;/span&gt;8086:293e&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;(&lt;/span&gt;rev 03&lt;span class='o'&gt;)&lt;/span&gt;
    00:1c.0 PCI bridge &lt;span class='o'&gt;[&lt;/span&gt;0604&lt;span class='o'&gt;]&lt;/span&gt;: Intel Corporation 82801I &lt;span class='o'&gt;(&lt;/span&gt;ICH9 Family&lt;span class='o'&gt;)&lt;/span&gt; PCI Express Port 1 &lt;span class='o'&gt;[&lt;/span&gt;8086:2940&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;(&lt;/span&gt;rev 03&lt;span class='o'&gt;)&lt;/span&gt;
    00:1c.1 PCI bridge &lt;span class='o'&gt;[&lt;/span&gt;0604&lt;span class='o'&gt;]&lt;/span&gt;: Intel Corporation 82801I &lt;span class='o'&gt;(&lt;/span&gt;ICH9 Family&lt;span class='o'&gt;)&lt;/span&gt; PCI Express Port 2 &lt;span class='o'&gt;[&lt;/span&gt;8086:2942&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;(&lt;/span&gt;rev 03&lt;span class='o'&gt;)&lt;/span&gt;
    00:1c.3 PCI bridge &lt;span class='o'&gt;[&lt;/span&gt;0604&lt;span class='o'&gt;]&lt;/span&gt;: Intel Corporation 82801I &lt;span class='o'&gt;(&lt;/span&gt;ICH9 Family&lt;span class='o'&gt;)&lt;/span&gt; PCI Express Port 4 &lt;span class='o'&gt;[&lt;/span&gt;8086:2946&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;(&lt;/span&gt;rev 03&lt;span class='o'&gt;)&lt;/span&gt;
    00:1d.0 USB Controller &lt;span class='o'&gt;[&lt;/span&gt;0c03&lt;span class='o'&gt;]&lt;/span&gt;: Intel Corporation 82801I &lt;span class='o'&gt;(&lt;/span&gt;ICH9 Family&lt;span class='o'&gt;)&lt;/span&gt; USB UHCI Controller &lt;span class='c'&gt;#1 [8086:2934] (rev 03)&lt;/span&gt;
    00:1d.1 USB Controller &lt;span class='o'&gt;[&lt;/span&gt;0c03&lt;span class='o'&gt;]&lt;/span&gt;: Intel Corporation 82801I &lt;span class='o'&gt;(&lt;/span&gt;ICH9 Family&lt;span class='o'&gt;)&lt;/span&gt; USB UHCI Controller &lt;span class='c'&gt;#2 [8086:2935] (rev 03)&lt;/span&gt;
    00:1d.2 USB Controller &lt;span class='o'&gt;[&lt;/span&gt;0c03&lt;span class='o'&gt;]&lt;/span&gt;: Intel Corporation 82801I &lt;span class='o'&gt;(&lt;/span&gt;ICH9 Family&lt;span class='o'&gt;)&lt;/span&gt; USB UHCI Controller &lt;span class='c'&gt;#3 [8086:2936] (rev 03)&lt;/span&gt;
    00:1d.7 USB Controller &lt;span class='o'&gt;[&lt;/span&gt;0c03&lt;span class='o'&gt;]&lt;/span&gt;: Intel Corporation 82801I &lt;span class='o'&gt;(&lt;/span&gt;ICH9 Family&lt;span class='o'&gt;)&lt;/span&gt; USB2 EHCI Controller &lt;span class='c'&gt;#1 [8086:293a] (rev 03)&lt;/span&gt;
    00:1e.0 PCI bridge &lt;span class='o'&gt;[&lt;/span&gt;0604&lt;span class='o'&gt;]&lt;/span&gt;: Intel Corporation 82801 Mobile PCI Bridge &lt;span class='o'&gt;[&lt;/span&gt;8086:2448&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;(&lt;/span&gt;rev 93&lt;span class='o'&gt;)&lt;/span&gt;
    00:1f.0 ISA bridge &lt;span class='o'&gt;[&lt;/span&gt;0601&lt;span class='o'&gt;]&lt;/span&gt;: Intel Corporation ICH9M-E LPC Interface Controller &lt;span class='o'&gt;[&lt;/span&gt;8086:2917&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;(&lt;/span&gt;rev 03&lt;span class='o'&gt;)&lt;/span&gt;
    00:1f.2 RAID bus controller &lt;span class='o'&gt;[&lt;/span&gt;0104&lt;span class='o'&gt;]&lt;/span&gt;: Intel Corporation Mobile 82801 SATA RAID Controller &lt;span class='o'&gt;[&lt;/span&gt;8086:282a&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;(&lt;/span&gt;rev 03&lt;span class='o'&gt;)&lt;/span&gt;
    00:1f.3 SMBus &lt;span class='o'&gt;[&lt;/span&gt;0c05&lt;span class='o'&gt;]&lt;/span&gt;: Intel Corporation 82801I &lt;span class='o'&gt;(&lt;/span&gt;ICH9 Family&lt;span class='o'&gt;)&lt;/span&gt; SMBus Controller &lt;span class='o'&gt;[&lt;/span&gt;8086:2930&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;(&lt;/span&gt;rev 03&lt;span class='o'&gt;)&lt;/span&gt;
    02:01.0 FireWire &lt;span class='o'&gt;(&lt;/span&gt;IEEE 1394&lt;span class='o'&gt;)&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;0c00&lt;span class='o'&gt;]&lt;/span&gt;: Ricoh Co Ltd R5C832 IEEE 1394 Controller &lt;span class='o'&gt;[&lt;/span&gt;1180:0832&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;(&lt;/span&gt;rev 05&lt;span class='o'&gt;)&lt;/span&gt;
    02:01.1 SD Host controller &lt;span class='o'&gt;[&lt;/span&gt;0805&lt;span class='o'&gt;]&lt;/span&gt;: Ricoh Co Ltd R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter &lt;span class='o'&gt;[&lt;/span&gt;1180:0822&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;(&lt;/span&gt;rev 22&lt;span class='o'&gt;)&lt;/span&gt;
    02:01.2 System peripheral &lt;span class='o'&gt;[&lt;/span&gt;0880&lt;span class='o'&gt;]&lt;/span&gt;: Ricoh Co Ltd R5C843 MMC Host Controller &lt;span class='o'&gt;[&lt;/span&gt;1180:0843&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;(&lt;/span&gt;rev ff&lt;span class='o'&gt;)&lt;/span&gt;
    0c:00.0 Network controller &lt;span class='o'&gt;[&lt;/span&gt;0280&lt;span class='o'&gt;]&lt;/span&gt;: Intel Corporation PRO/Wireless 5300 AGN &lt;span class='o'&gt;[&lt;/span&gt;Shiloh&lt;span class='o'&gt;]&lt;/span&gt; Network Connection &lt;span class='o'&gt;[&lt;/span&gt;8086:4235&lt;span class='o'&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;The following is a summary of major components and how they performed, along with any work I had to perform to get them working. I was absolutely blown away by how everything just worked. Linux compatability for Linux has come a long way in the last 3 years.&lt;/p&gt;

&lt;h3 id='displaygraphics'&gt;Display/Graphics&lt;/h3&gt;

&lt;p&gt;The Intel GS45 Express graphics card works fine in xorg with the xserver-xorg-video-intel package. No /etc/X11/xorg.conf file is necesary.&lt;/p&gt;

&lt;p&gt;Thanks to the open source intel drivers, xrandr 1.2 works really well. Using a package like arandr, you can add or remove external monitors and change resolutions, all without restarting X.&lt;/p&gt;

&lt;h3 id='wired_ethernet'&gt;Wired Ethernet&lt;/h3&gt;

&lt;p&gt;The 82567LM Intel gigabit network card works with the e1000 kernel driver.&lt;/p&gt;

&lt;h3 id='wireless_network'&gt;Wireless Network&lt;/h3&gt;

&lt;p&gt;The 5300 Intel wireless card works once the firmware-iwlwifi package has been installed. This is a non-free package, make sure you have added non-free to your apt sources.&lt;/p&gt;

&lt;h3 id='trackpad'&gt;Trackpad&lt;/h3&gt;

&lt;p&gt;The trackpad works under X with no customisations.&lt;/p&gt;

&lt;h3 id='power'&gt;Power&lt;/h3&gt;

&lt;p&gt;Suspend, resume and hibernate all work fine. A refreshing change after 3 years of having no support for these at all. I suspect the binary NVidia drivers were to blame, so I&amp;#8217;m &lt;em&gt;more&lt;/em&gt; than happy with my decision to get Intel hardware.&lt;/p&gt;

&lt;h3 id='audio'&gt;Audio&lt;/h3&gt;

&lt;p&gt;Alsa audio worked with no customisations. I did need to unmute the default channel.&lt;/p&gt;

&lt;h3 id='firewire'&gt;Firewire&lt;/h3&gt;

&lt;p&gt;The firewire port worked with no customisations.&lt;/p&gt;

&lt;h3 id='kernel'&gt;Kernel&lt;/h3&gt;

&lt;p&gt;I upgraded to the current kernel in Sid (2.6.31+22) with no issues.&lt;/p&gt;</description>
  </item>

  <item>
    <title>Prawn And X Accel Redirect</title>
    <link>http://yob.id.au/2009/05/30/prawn-and-x-accel-redirect.html</link>
    <guid>http://yob.id.au/2009/05/30/prawn-and-x-accel-redirect</guid>
    <updated>2009-05-30T00:00:00+10:00</updated>
    <description>&lt;p&gt;&lt;a href='http://prawn.majesticseacreature.com/'&gt;Prawn&lt;/a&gt; is a pure Ruby PDF generation library. &lt;a href='http://www.cracklabs.com/prawnto'&gt;PrawnTo&lt;/a&gt; is a rails plugin that makes it dead easy to add PDF views to standard rails actions.&lt;/p&gt;

&lt;p&gt;I needed to generate a significant number of PDF reports in an app I support and although Prawnto makes things simple I had a few requirements that meant it wasn&amp;#8217;t a good fit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I wanted to share my reports across several projects&lt;/li&gt;

&lt;li&gt;I wanted to be able to generate PDFs in situations that weren&amp;#8217;t responses to a HTTP request. For example, for attaching to an email or as part of a background job&lt;/li&gt;

&lt;li&gt;I didn&amp;#8217;t want downloads of large PDFs to tie up one of my rails processes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these in mind I came up with an alternative (and slightly more complex) technique for using Prawn in my app. The key features are storing the report definitions in their own directory (to allow sharing between projects using git sub modules) and using nginx&amp;#8217;s X-Accel-Redirect feature to let nginx handle streaming the generated file to the client. You can read a little about this feature in the &lt;a href='http://wiki.nginx.org//NginxXSendfile'&gt;nginx wiki&lt;/a&gt;. Apache has a similar feature called X-Sendfile that is roughly comparable.&lt;/p&gt;

&lt;p&gt;There are a number of steps involved in getting this technique running, so I will assume you are familiar with both Prawn, nginx and Rails. I have code snippets inline, or you can view a full sample application at &lt;a href='https://github.com/yob/prawn-rails-xaccelredirect/tree'&gt;github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Start by editing your config/enviroment.rb file to add the following three lines. The first tells Rails about the new directory we&amp;#8217;ll use to store our reports and the next 2 load prawn.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;    &lt;span class='n'&gt;config&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;load_paths&lt;/span&gt; &lt;span class='o'&gt;+=&lt;/span&gt; &lt;span class='sx'&gt;%W( &lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='no'&gt;RAILS_ROOT&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='sx'&gt;/app/reports )&lt;/span&gt;
    &lt;span class='n'&gt;config&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;gem&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;prawn&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:version&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;0.4.1&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:lib&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;prawn&amp;quot;&lt;/span&gt;
    &lt;span class='n'&gt;config&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;gem&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;prawn-format&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:version&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;0.1.1&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:lib&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;prawn/format&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Next, edit or create config/initializers/mime_types.rb and add a line to register the PDF mime type:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;    &lt;span class='no'&gt;Mime&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Type&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;register&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;application/pdf&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:pdf&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now, create a new class in app/reports/application_report.rb. This class will be a superclass for all the reports you will be generating.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;    &lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;pathname&amp;#39;&lt;/span&gt;

    &lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;ApplicationReport&lt;/span&gt;

      &lt;span class='kp'&gt;attr_reader&lt;/span&gt; &lt;span class='ss'&gt;:path&lt;/span&gt;

      &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;render&lt;/span&gt;
        &lt;span class='nb'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;__send__&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:render_header&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='nb'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;respond_to?&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:render_header&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
        &lt;span class='nb'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;__send__&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:render_body&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;   &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='nb'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;respond_to?&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:render_body&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
        &lt;span class='nb'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;__send__&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:render_footer&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='nb'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;respond_to?&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:render_footer&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
        &lt;span class='no'&gt;File&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;open&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;tmp_file&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;w&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;f&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;f&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;write&lt;/span&gt; &lt;span class='n'&gt;pdf&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;render&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
        &lt;span class='nb'&gt;self&lt;/span&gt;
      &lt;span class='k'&gt;end&lt;/span&gt;

      &lt;span class='kp'&gt;private&lt;/span&gt;

      &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;pdf&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;opts&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;{})&lt;/span&gt;
        &lt;span class='vi'&gt;@path&lt;/span&gt; &lt;span class='o'&gt;||=&lt;/span&gt; &lt;span class='n'&gt;tmp_file&lt;/span&gt;
        &lt;span class='vi'&gt;@pdf&lt;/span&gt; &lt;span class='o'&gt;||=&lt;/span&gt; &lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Prawn&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Document&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;opts&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
      &lt;span class='k'&gt;end&lt;/span&gt;

      &lt;span class='c1'&gt;# choose a file to render our PDF to before sending it to the user&lt;/span&gt;
      &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;tmp_file&lt;/span&gt;
        &lt;span class='n'&gt;counter&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='mi'&gt;1&lt;/span&gt;
        &lt;span class='n'&gt;path&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kp'&gt;nil&lt;/span&gt;
        &lt;span class='n'&gt;dir&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;RAILS_ROOT&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;/tmp/pdfs&amp;quot;&lt;/span&gt;
        &lt;span class='no'&gt;FileUtils&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;mkdir_p&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;dir&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
        &lt;span class='n'&gt;dir&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Pathname&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;dir&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;realpath&lt;/span&gt;
        &lt;span class='k'&gt;while&lt;/span&gt; &lt;span class='n'&gt;path&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;nil?&lt;/span&gt; &lt;span class='o'&gt;||&lt;/span&gt; &lt;span class='no'&gt;File&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;file?&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;path&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
          &lt;span class='n'&gt;path&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;dir&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;/pdf-&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;counter&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;
          &lt;span class='n'&gt;counter&lt;/span&gt; &lt;span class='o'&gt;+=&lt;/span&gt; &lt;span class='mi'&gt;1&lt;/span&gt;
        &lt;span class='k'&gt;end&lt;/span&gt;
        &lt;span class='n'&gt;path&lt;/span&gt;
      &lt;span class='k'&gt;end&lt;/span&gt;
    &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now, create a subclass of ApplicationReport in app/reports/product_report.rb. This will be your first real report. I&amp;#8217;ve created a sample one here called ProductReport to display the basic information from my Product model. You will need to modify it to suit your application.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;    &lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;ProductReport&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='no'&gt;ApplicationReport&lt;/span&gt;
      &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;initialize&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;product&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
        &lt;span class='vi'&gt;@product&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;product&lt;/span&gt;
      &lt;span class='k'&gt;end&lt;/span&gt;

      &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;render_header&lt;/span&gt;
        &lt;span class='n'&gt;pdf&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;font_size&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;16&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
          &lt;span class='n'&gt;pdf&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;text&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;&amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='vi'&gt;@product&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;description&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;&amp;lt;/b&amp;gt;&amp;quot;&lt;/span&gt;
        &lt;span class='k'&gt;end&lt;/span&gt;
      &lt;span class='k'&gt;end&lt;/span&gt;

      &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;render_body&lt;/span&gt;
        &lt;span class='n'&gt;pdf&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;font_size&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;12&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
          &lt;span class='n'&gt;pdf&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;text&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;&amp;lt;b&amp;gt;Code: &amp;lt;/b&amp;gt;&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='vi'&gt;@product&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;code&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;
          &lt;span class='n'&gt;pdf&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;text&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;&amp;lt;b&amp;gt;RRP: &amp;lt;/b&amp;gt;&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='vi'&gt;@product&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;rrp&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;
        &lt;span class='k'&gt;end&lt;/span&gt;
      &lt;span class='k'&gt;end&lt;/span&gt;
    &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Next, we need to setup nginx to use the X-Accel-Redirect feature. Add the following four lines to your nginx config file, changing the path to point to your own rails app directory. For more information on this step, consult google. There&amp;#8217;s plenty of relevant information around.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;location /srv/rails/mypp/tmp/pdfs {
  internal;
  root /;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To simplify using X-Accel-Redirect, add two helper methods to your ApplicationController:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;    &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;x_accel_pdf&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;path&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;filename&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
      &lt;span class='n'&gt;x_accel_redirect&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;path&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:type&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;application/pdf&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:filename&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;filename&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='k'&gt;end&lt;/span&gt;

    &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;x_accel_redirect&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;path&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;opts&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='p'&gt;{})&lt;/span&gt;
      &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;opts&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:type&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
        &lt;span class='n'&gt;response&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;headers&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;Content-Type&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;opts&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:type&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
      &lt;span class='k'&gt;else&lt;/span&gt;
        &lt;span class='n'&gt;response&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;headers&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;Content-Type&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;application/octet-stream&amp;quot;&lt;/span&gt;
      &lt;span class='k'&gt;end&lt;/span&gt;
      &lt;span class='n'&gt;response&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;headers&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;Content-Disposition&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;attachment;&amp;quot;&lt;/span&gt;
      &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;opts&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:filename&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
        &lt;span class='n'&gt;response&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;headers&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;Content-Disposition&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot; filename= &lt;/span&gt;&lt;span class='se'&gt;\&amp;quot;&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;opts&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:filename&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='se'&gt;\&amp;quot;&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;
      &lt;span class='k'&gt;end&lt;/span&gt;
      &lt;span class='n'&gt;response&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;headers&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;X-Accel-Redirect&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;path&lt;/span&gt;

      &lt;span class='no'&gt;Rails&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;logger&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;info&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;path&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt; sent to client using X-Accel-Redirect&amp;quot;&lt;/span&gt;

      &lt;span class='n'&gt;render&lt;/span&gt; &lt;span class='ss'&gt;:nothing&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='kp'&gt;true&lt;/span&gt;
    &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Finally, add the rendering and X-Accel-Redirect instructions to the relevant controller action. Since my sample report earlier was to display a single product, I&amp;#8217;ve added it to the show action on my ProductsController.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;    &lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;ProductsController&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='no'&gt;ApplicationController&lt;/span&gt;

      &lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;.&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;

      &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;show&lt;/span&gt;
        &lt;span class='vi'&gt;@product&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Product&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;find&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;params&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:id&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;

        &lt;span class='n'&gt;respond_to&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='nb'&gt;format&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
          &lt;span class='nb'&gt;format&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;html&lt;/span&gt;
          &lt;span class='nb'&gt;format&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;pdf&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
            &lt;span class='n'&gt;report&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;ProductReport&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='vi'&gt;@product&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
            &lt;span class='n'&gt;report&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;render&lt;/span&gt;
            &lt;span class='n'&gt;x_accel_pdf&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;report&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;path&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;product-&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='vi'&gt;@product&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;id&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;.pdf&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
          &lt;span class='k'&gt;end&lt;/span&gt;
        &lt;span class='k'&gt;end&lt;/span&gt;
      &lt;span class='k'&gt;end&lt;/span&gt;

      &lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;.&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;

    &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The key lines for generating a report are:&lt;/p&gt;

&lt;p&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;    &lt;span class='n'&gt;report&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;ProductReport&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='vi'&gt;@product&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='n'&gt;report&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;render&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;I can call these lines any time I need to generate my ProductReport, whether it be in a controller action like here, a ActionMailer email, or a rake task. The output will be rendered to disk and I can retrieve the path by calling report.path.&lt;/p&gt;

&lt;p&gt;The key line for using X-Accel-Redirect is:&lt;/p&gt;

&lt;p&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;    &lt;span class='n'&gt;x_accel_pdf&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;report&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;path&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;product-&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='vi'&gt;@product&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;id&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;.pdf&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;I can call x_accel_pdf() in any controller action and the filename I give it will be streamed to the client by nginx instead of my rails app.&lt;/p&gt;

&lt;p&gt;The setup to all this is a little involved, but once it&amp;#8217;s in your app adding new reports is dead simple. A worthwhile tradeoff in my opinion.&lt;/p&gt;

&lt;p&gt;The most likely problem you&amp;#8217;re likely to run into is misconfiguring nginx. For hints on what might be wrong, make sure you check out the nginx error log.&lt;/p&gt;</description>
  </item>

  <item>
    <title>Santising Activerecord String Attributes</title>
    <link>http://yob.id.au/2009/05/22/santising-activerecord-string-attributes.html</link>
    <guid>http://yob.id.au/2009/05/22/santising-activerecord-string-attributes</guid>
    <updated>2009-05-22T00:00:00+10:00</updated>
    <description>&lt;p&gt;For the vast majority of string attributes on my ActiveRecord models, I want a blank string submitted by the user to be saved to the database as NULL. I started writing a small Rails plugin to achieve my goal via a before_validation callback, but then found an existing plugin, Ryan McGeary&amp;#8217;s &lt;a href='http://github.com/rmm5t/strip_attributes'&gt;StripAttributes&lt;/a&gt; that does the job just fine. As an added bonus it removes leading and trailing whitespace as well.&lt;/p&gt;

&lt;p&gt;More recently I was processing a large volume of CSV data and creating new AR records. It turned out the data had some odd &lt;a href='http://asciitable.com/'&gt;ASCII control characters&lt;/a&gt; in it (like 0x0B - vertical tab) that were causing grief elsewhere in the app.&lt;/p&gt;

&lt;p&gt;In this day and age it&amp;#8217;s rare to require the use of ASCII control characters, so I built a new plugin to strip them out before a model is saved. The plugin is essentially a fork of StripAttributes so I won&amp;#8217;t claim it&amp;#8217;s innovative, but it&amp;#8217;s useful.&lt;/p&gt;

&lt;p&gt;&lt;a href='http://github.com/yob/strip_control_chars'&gt;StripControlChars&lt;/a&gt; can be found on github and installed as a gem.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='sh'&gt;    gem install strip_control_chars
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Load the gem from your environment.rb file:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;    &lt;span class='n'&gt;config&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;gem&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;strip_control_chars&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;And add the macro to the models you want to use it with:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;    &lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;Product&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='no'&gt;ActiveRecord&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Base&lt;/span&gt;
      &lt;span class='n'&gt;strip_control_chars!&lt;/span&gt;
    &lt;span class='k'&gt;end&lt;/span&gt;

    &lt;span class='nb'&gt;p&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Product&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;create!&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:name&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Some&lt;/span&gt;&lt;span class='se'&gt;\x0B&lt;/span&gt;&lt;span class='s2'&gt;Widget&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='nb'&gt;puts&lt;/span&gt; &lt;span class='nb'&gt;p&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;name&lt;/span&gt;
    &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='no'&gt;Some&lt;/span&gt; &lt;span class='no'&gt;Widget&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The following bytes will be replaced with a standard space (0x20): 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0B, 0x0C, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F&lt;/p&gt;

&lt;p&gt;Whilst I was making plugins based on StripAttributes, I made another that replaces various &amp;#8220;smart quotes&amp;#8221; with their ASCII equivalents. It&amp;#8217;s somewhat more controversial than StripControlChars and will probably get me in trouble with typographers, but them&amp;#8217;s the breaks. It operates in the same way as StripControlChars, so doesn&amp;#8217;t really deserve its own blog post. Check it out on github: &lt;a href='https://github.com/yob/dumb_quotes'&gt;DumbQuotes&lt;/a&gt;&lt;/p&gt;</description>
  </item>

  <item>
    <title>Eventmachine Based Ftp Server</title>
    <link>http://yob.id.au/2009/03/21/eventmachine-based-ftp-server.html</link>
    <guid>http://yob.id.au/2009/03/21/eventmachine-based-ftp-server</guid>
    <updated>2009-03-21T00:00:00+11:00</updated>
    <description>&lt;h3 id='the_short_version'&gt;The short version&lt;/h3&gt;

&lt;p&gt;I&amp;#8217;ve released a demo of an FTP server for ruby, built on the EventMachine library.&lt;/p&gt;

&lt;p&gt;Check it out here: &lt;a href='http://github.com/yob/em-ftpd/tree'&gt;http://github.com/yob/em-ftpd/tree&lt;/a&gt;&lt;/p&gt;

&lt;h3 id='the_full_version'&gt;The full version&lt;/h3&gt;

&lt;p&gt;Inside our little web 2.0 bubble we all get very excited about shiny things like REST, web services and open JSON APIs, so it&amp;#8217;s easy to forget in the business world with many trading partners and legacy systems, a technology that works tends to hang around for an awfully long time.&lt;/p&gt;

&lt;p&gt;In the supply chain world, the most common way I&amp;#8217;ve encountered for electronic trading between businesses (submitting orders, receiving invoices, etc) is over good &amp;#8216;ole &lt;a href='http://tools.ietf.org/rfc/rfc959.txt'&gt;FTP&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Yes, it&amp;#8217;s old and insecure. However it&amp;#8217;s also simple, well understood, has plenty of library support and is now embedded in inventory management software all over the place. You work with what you&amp;#8217;ve got.&lt;/p&gt;

&lt;p&gt;For a few years I&amp;#8217;ve maintained a custom FTP server for a client. It accepts uploaded orders from their customers, translates them to a format the clients system can understand and saves a copy to a outbox folder. Every few hours the clients inventory software connects and downloads the orders directly into their system. It&amp;#8217;s hardly a high traffic site, maybe 20 transfers a day, all small text files.&lt;/p&gt;

&lt;p&gt;The server was a small ruby daemon, based on the &lt;a href='http://rubyforge.org/projects/ftpd/'&gt;ftpd.rb&lt;/a&gt; script published by Chris Wanstrath, and it performed admirably. Recently I was asked to add some new features, and I realised over the years the code had turned into 800 lines of spaghetti. A rewrite was in order.&lt;/p&gt;

&lt;p&gt;At the same time a few fellow Australian Rubyist&amp;#8217;s had been &lt;a href='http://gist.github.com/81523/'&gt;playing around&lt;/a&gt; with &lt;a href='http://rubyeventmachine.com/'&gt;EventMachine&lt;/a&gt;, a library that claims to help produce highly scalable network code. Scalability wasn&amp;#8217;t my issue, but the examples I could see looked alarmingly clean and simple for network socket based applications. Best of all, there was no need to involve threads, the arch nemesis of maintainable code.&lt;/p&gt;

&lt;p&gt;Clean and simple was the goal of my rewrite. Bingo.&lt;/p&gt;

&lt;p&gt;It took a little while to come to a working solution, mainly because I found few public EventMachine samples that indicated how to open multiple sockets and share data between them. It turned out to be possible and my rewritten server went into production a few days ago.&lt;/p&gt;

&lt;p&gt;There was no point asking this client to release their new server as open source, it&amp;#8217;s fairly specific to their needs. However I was keen to provide a public demo of an EventMachine based FTP server, so I&amp;#8217;ve published a new demo script on github: &lt;a href='http://github.com/yob/em-ftpd/tree'&gt;http://github.com/yob/em-ftpd/tree&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It&amp;#8217;s not particularly useful as a real FTP server - it hard codes the authentication tokens and provides a simulated directory structure. Hopefully it proves interesting or useful to someone out there though - as a basis for their own custom FTP server or some other multi-socket protocol.&lt;/p&gt;</description>
  </item>

  <item>
    <title>Outdated Gems Under Rails The Second</title>
    <link>http://yob.id.au/2009/02/27/outdated-gems-under-rails-the-second.html</link>
    <guid>http://yob.id.au/2009/02/27/outdated-gems-under-rails-the-second</guid>
    <updated>2009-02-27T00:00:00+11:00</updated>
    <description>&lt;p&gt;Last year I &lt;a href='/blog/2008/06/23/outdated_gems_under_rails'&gt;shared a short initialiser fragment&lt;/a&gt; for rails that warns you if you&amp;#8217;ve loaded an out of date gem.&lt;/p&gt;

&lt;p&gt;It&amp;#8217;s a non fatal warning, just a reminder that it might be worthwhile upgrading.&lt;/p&gt;

&lt;p&gt;I still use the fragment, but have updated it to support rails 2.2+. To use it, just drop the code into a file in the config/initializers directory of your app.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;    &lt;span class='c1'&gt;# *************************************&lt;/span&gt;
    &lt;span class='c1'&gt;# A handy initiliser that logs when the loaded version of&lt;/span&gt;
    &lt;span class='c1'&gt;# Rails or a gem dependency is out of date. The notice is&lt;/span&gt;
    &lt;span class='c1'&gt;# non-fatal (often we want it to be out of date). I often&lt;/span&gt;
    &lt;span class='c1'&gt;# forget which version of a gem my apps are using, and &lt;/span&gt;
    &lt;span class='c1'&gt;# don&amp;#39;t notice when there is a newer version available.&lt;/span&gt;
    &lt;span class='c1'&gt;#&lt;/span&gt;
    &lt;span class='c1'&gt;# Only really makes sense on Rails &amp;gt;= 2.1, where initialisers&lt;/span&gt;
    &lt;span class='c1'&gt;# and gem dependencies first appeared. Drop this file in&lt;/span&gt;
    &lt;span class='c1'&gt;# config/initializers/&lt;/span&gt;
    &lt;span class='c1'&gt;#&lt;/span&gt;
    &lt;span class='c1'&gt;# James Healy&lt;/span&gt;
    &lt;span class='c1'&gt;# 2nd Feb 2009&lt;/span&gt;
    &lt;span class='c1'&gt;# *************************************&lt;/span&gt;

    &lt;span class='n'&gt;outdated&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='o'&gt;[]&lt;/span&gt;

    &lt;span class='c1'&gt;# *************************************&lt;/span&gt;
    &lt;span class='c1'&gt;# check the current version of Rails to see if it&amp;#39;s the latest&lt;/span&gt;
    &lt;span class='c1'&gt;# *************************************&lt;/span&gt;
    &lt;span class='n'&gt;max_rails_gem&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Gem&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;cache&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;find_name&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;rails&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;map&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='ss'&gt;:version&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;map&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='ss'&gt;:version&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;max&lt;/span&gt;

    &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;max_rails_gem&lt;/span&gt; &lt;span class='o'&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;Rails&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;VERSION&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;STRING&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='n'&gt;max_rails_gem&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
      &lt;span class='n'&gt;outdated&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='ss'&gt;:name&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;rails&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:loaded&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='no'&gt;Rails&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;VERSION&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;STRING&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:max&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;max_rails_gem&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
    &lt;span class='k'&gt;end&lt;/span&gt;

    &lt;span class='c1'&gt;# *************************************&lt;/span&gt;
    &lt;span class='c1'&gt;# check the current version of all required gems to see if they&amp;#39;re the latest&lt;/span&gt;
    &lt;span class='c1'&gt;# *************************************&lt;/span&gt;
    &lt;span class='no'&gt;Rails&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;configuration&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;gems&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;each&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;gem&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
      &lt;span class='nb'&gt;name&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;gem&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;name&lt;/span&gt;
      &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='no'&gt;Rails&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;VERSION&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;STRING&lt;/span&gt; &lt;span class='o'&gt;&amp;gt;=&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;2.2.0&amp;quot;&lt;/span&gt;
        &lt;span class='n'&gt;loaded_version&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;gem&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;specification&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;version&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to_s&lt;/span&gt;
      &lt;span class='k'&gt;else&lt;/span&gt;
        &lt;span class='n'&gt;loaded_version&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;gem&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;version&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to_s&lt;/span&gt;
      &lt;span class='k'&gt;end&lt;/span&gt;
      &lt;span class='n'&gt;max_gem_version&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Gem&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;cache&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;find_name&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;name&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;map&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='ss'&gt;:version&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;map&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='ss'&gt;:version&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;max&lt;/span&gt;

      &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;max_gem_version&lt;/span&gt; &lt;span class='o'&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;loaded_version&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='n'&gt;max_gem_version&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
        &lt;span class='n'&gt;outdated&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='ss'&gt;:name&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='nb'&gt;name&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:loaded&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;loaded_version&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:max&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;max_gem_version&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
      &lt;span class='k'&gt;end&lt;/span&gt;
    &lt;span class='k'&gt;end&lt;/span&gt;

    &lt;span class='c1'&gt;# *************************************&lt;/span&gt;
    &lt;span class='c1'&gt;# print notices &lt;/span&gt;
    &lt;span class='c1'&gt;# *************************************&lt;/span&gt;
    &lt;span class='k'&gt;unless&lt;/span&gt; &lt;span class='n'&gt;outdated&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;empty?&lt;/span&gt;
      &lt;span class='n'&gt;logger&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;RAILS_DEFAULT_LOGGER&lt;/span&gt;
      &lt;span class='n'&gt;logger&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;info&lt;/span&gt; 
      &lt;span class='n'&gt;logger&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;info&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;*******************************&amp;quot;&lt;/span&gt;
      &lt;span class='n'&gt;outdated&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;each&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;w&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
        &lt;span class='n'&gt;logger&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;info&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;NOTICE: &lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;w&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:name&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt; version &lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;w&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:loaded&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt; is not the most recent version of &lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;w&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:name&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt; available on the system (&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;w&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:max&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;)&amp;quot;&lt;/span&gt;
      &lt;span class='k'&gt;end&lt;/span&gt;
      &lt;span class='n'&gt;logger&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;info&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;*******************************&amp;quot;&lt;/span&gt;
      &lt;span class='n'&gt;logger&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;info&lt;/span&gt; 
    &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;</description>
  </item>

</rss>
