<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Peter's Blog - Nodes for rails</title>
    <link>http://www.petersblog.org/</link>
    <description>Nodes containing the tag rails</description>
    <item>
      <title>Getting back up to speed</title>
      <link>http://www.petersblog.org/node/view/1660</link>
      <description>&lt;p&gt;
I was working on this site and noticed how slow it was, particularly on rss feeds. This was just after managing to completely screw up my ruby gem installation (apt-get remove rubygems/apt-get install rubygems didn't reinstall rubygems) which I only cured by upgrading the server to ubuntu hardy. I wasn't sure whether I had blown something in the upgrade (the server did a fsck when I rebooted it but maybe only because it had been 'up' for 100 days) or whether the code was just slow. 
&lt;/p&gt;
&lt;p&gt;
How to pinpoint the cause of the slowness? Profiling. Some research revealed I can profile ruby code as follows: 
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;
install a ruby profiler: 
&lt;pre class="lazy"&gt;gen install ruby-prof
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
insert some profiling code around your suspect slow code (in my case around the rss formatting): 
&lt;pre class="lazy"&gt;&lt;span class="line-numbers"&gt;   1 &lt;/span&gt; gem install ruby&lt;span class="Keyword"&gt;-&lt;/span&gt;prof
&lt;span class="line-numbers"&gt;   2 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;   3 &lt;/span&gt;    &lt;span class="Keyword"&gt;require&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;ruby-prof&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   4 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;   5 &lt;/span&gt;    &lt;span class="Support"&gt;RubyProf&lt;/span&gt;.&lt;span class="Entity"&gt;start&lt;/span&gt;
&lt;span class="line-numbers"&gt;   6 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;   7 &lt;/span&gt;    {code to profile}
&lt;span class="line-numbers"&gt;   8 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;   9 &lt;/span&gt;  result &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Support"&gt;RubyProf&lt;/span&gt;.&lt;span class="Entity"&gt;stop&lt;/span&gt;
&lt;span class="line-numbers"&gt;  10 &lt;/span&gt;    printer &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Support"&gt;RubyProf&lt;/span&gt;::&lt;span class="Entity"&gt;FlatPrinter&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt;(result)
&lt;span class="line-numbers"&gt;  11 &lt;/span&gt;    printer.&lt;span class="Entity"&gt;print&lt;/span&gt;(&lt;span class="Support"&gt;File&lt;/span&gt;.&lt;span class="Entity"&gt;open&lt;/span&gt;( &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;/tmp/prof.txt&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;w&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;), &lt;span class="Constant"&gt;0&lt;/span&gt;)
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
run script/server and try downloading rss 
&lt;/li&gt;
&lt;li&gt;
wait because it is very slow 
&lt;/li&gt;
&lt;li&gt;
examine the profile 
&lt;pre class="lazy"&gt;vim /tmp/prof.txt
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
lament 
&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;
This told me that 70% of my time was being spent in a gsub in my &lt;a href="/tag/wilki"&gt;wilki&lt;/a&gt; code that I &lt;a href="/node/1658"&gt;added last week&lt;/a&gt;. gsub was being called 1800 times (!) and when I commented out the new gsub call the rss was rendered much faster. 
&lt;/p&gt;
&lt;p&gt;
The thing is, my wilki formatting is supposed to be cached, it should only be done when a posting is modified, not every time the post is rendered. Turns out to be because updating a field in an ActiveRecord doesn't automatically cause it to be saved because it is dirty, an explicit save helped. Problem solved, now downloading the &lt;a href="/blog/feed/1"&gt;rss feed&lt;/a&gt; doesn't max out my cpu for 20 seconds &lt;img alt="smile" src="/images/smileys/smile.png" /&gt; 
&lt;/p&gt;
&lt;p&gt;
NB (clarification): the rss doesn't go through the rails fragment cache. 
&lt;/p&gt;&lt;p&gt;Related Posts: &lt;a href="/tag/nooby"&gt;nooby&lt;/a&gt; &lt;a href="/tag/rails"&gt;rails&lt;/a&gt; &lt;a href="/tag/ruby"&gt;ruby&lt;/a&gt;&lt;/p&gt;</description>
      <guid>http://www.petersblog.org/node/view/1660</guid>
      <category domain="http://www.technorati.com/tag">nooby</category>
      <category domain="http://www.technorati.com/tag">rails</category>
      <category domain="http://www.technorati.com/tag">ruby</category>
    </item>
    <item>
      <title>How big would you like that?</title>
      <link>http://www.petersblog.org/node/view/1658</link>
      <description>&lt;p&gt;
My websites are all migrating to a CMS kinda vibe where pages are edited through the web browser. Easy for me, easy for anyone else. I've already got &lt;a href="/node/1620"&gt;the technology to upload pictures&lt;/a&gt; but it has an annoying problem and it seems to be built into the attachment_fu plugin. The problem is that one must choose how big one wants the pictures to be when they are first uploaded and the size is defined in the database model. attachment_fu will then resize the image accordingly. That is all very well if one is organised and prepared and knows in advance how big they want their pictures or are running a simple gallery where all pictures are the same size. 
&lt;/p&gt;
&lt;p&gt;
But what about where one is trying to do some nice layouts on a page and wants to adjust the sizes of the pictures? Say I decide I want a particular picture on a page to be 100 pixels wide? I have two options: 
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;
I can go into gimp/paintshop pro/whatever, resize the image and upload it (again) but that is annoying. 
&lt;/li&gt;
&lt;li&gt;
I can alter the img tags in my html to include a width attribute. I see a lot of this on other websites, as the page downloads you see lots of little images slowly filled in as huge 1000x1000 jpg's are downloaded and resized in the browser to 100x100 because the page designer couldn't be bothered to resize the image in gimp/paintshop pro/whatever. 
&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;
Now there is another way. Here is a &lt;a href="/tag/rails"&gt;rails&lt;/a&gt; helper that allows the width of an image to be specified in a view, automatically resizing the image as appropriate: 
&lt;/p&gt;
&lt;pre class="lazy"&gt;&lt;span class="line-numbers"&gt;   1 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; Methods added to this helper will be available to all templates in the application.&lt;/span&gt;
&lt;span class="line-numbers"&gt;   2 &lt;/span&gt; &lt;span class="Keyword"&gt;module&lt;/span&gt; &lt;span class="Entity"&gt;ApplicationHelper&lt;/span&gt;
&lt;span class="line-numbers"&gt;   3 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;   4 &lt;/span&gt; &lt;span class="Comment"&gt;   &lt;span class="Comment"&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   5 &lt;/span&gt; &lt;span class="Comment"&gt;   &lt;span class="Comment"&gt;#&lt;/span&gt; Takes the url of an image and a desired width and ensures a cached resized&lt;/span&gt;
&lt;span class="line-numbers"&gt;   6 &lt;/span&gt; &lt;span class="Comment"&gt;   &lt;span class="Comment"&gt;#&lt;/span&gt; version of the image is available and returns the url of said image.&lt;/span&gt;
&lt;span class="line-numbers"&gt;   7 &lt;/span&gt; &lt;span class="Comment"&gt;   &lt;span class="Comment"&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   8 &lt;/span&gt; &lt;span class="Comment"&gt;   &lt;span class="Comment"&gt;#&lt;/span&gt; the url should not be a relative url as it is assumed work relative to RAILS_ROOT/public&lt;/span&gt;
&lt;span class="line-numbers"&gt;   9 &lt;/span&gt; &lt;span class="Comment"&gt;   &lt;span class="Comment"&gt;#&lt;/span&gt; This also means the image must be on this server.&lt;/span&gt;
&lt;span class="line-numbers"&gt;  10 &lt;/span&gt; &lt;span class="Comment"&gt;   &lt;span class="Comment"&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  11 &lt;/span&gt;    &lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;cacheimage&lt;/span&gt;(&lt;span class="Variable"&gt; strUrl&lt;span class="Variable"&gt;,&lt;/span&gt; nWidth&lt;/span&gt;)
&lt;span class="line-numbers"&gt;  12 &lt;/span&gt; &lt;span class="Comment"&gt;     &lt;span class="Comment"&gt;#&lt;/span&gt; Find original file in file system&lt;/span&gt;
&lt;span class="line-numbers"&gt;  13 &lt;/span&gt;      strFile &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;&lt;span class="Variable"&gt;RAILS_ROOT&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;/public&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="Keyword"&gt;+&lt;/span&gt; strUrl
&lt;span class="line-numbers"&gt;  14 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  15 &lt;/span&gt; &lt;span class="Comment"&gt;     &lt;span class="Comment"&gt;#&lt;/span&gt; create name for cached and resized image&lt;/span&gt;
&lt;span class="line-numbers"&gt;  16 &lt;/span&gt; &lt;span class="Comment"&gt;     &lt;span class="Comment"&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  17 &lt;/span&gt; &lt;span class="Comment"&gt;     &lt;span class="Comment"&gt;#&lt;/span&gt; /blah/blah.jpg --&amp;gt; /blah/blah-123.jpg&lt;/span&gt;
&lt;span class="line-numbers"&gt;  18 &lt;/span&gt; &lt;span class="Comment"&gt;     &lt;span class="Comment"&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  19 &lt;/span&gt;      strBits &lt;span class="Keyword"&gt;=&lt;/span&gt; strUrl.&lt;span class="Entity"&gt;split&lt;/span&gt;( &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;.&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;)
&lt;span class="line-numbers"&gt;  20 &lt;/span&gt;      strBits[&lt;span class="Keyword"&gt;-&lt;/span&gt;&lt;span class="Constant"&gt;2&lt;/span&gt;] &lt;span class="Keyword"&gt;+=&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;-&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;nWidth&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  21 &lt;/span&gt;      strCacheName &lt;span class="Keyword"&gt;=&lt;/span&gt; strBits.&lt;span class="Entity"&gt;join&lt;/span&gt;( &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;.&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;)
&lt;span class="line-numbers"&gt;  22 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  23 &lt;/span&gt;      strCacheUrl  &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;/imagecache&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;strCacheName&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  24 &lt;/span&gt;      strCacheFileName &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;&lt;span class="Variable"&gt;RAILS_ROOT&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;/public&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;strCacheUrl&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  25 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  26 &lt;/span&gt;      &lt;span class="Keyword"&gt;if&lt;/span&gt; &lt;span class="Keyword"&gt;not&lt;/span&gt; &lt;span class="Support"&gt;File&lt;/span&gt;.&lt;span class="Entity"&gt;exists?&lt;/span&gt;( strCacheFileName)
&lt;span class="line-numbers"&gt;  27 &lt;/span&gt;        &lt;span class="Keyword"&gt;require&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;image_science&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  28 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  29 &lt;/span&gt;        &lt;span class="Support"&gt;FileUtils&lt;/span&gt;.&lt;span class="Entity"&gt;mkpath&lt;/span&gt;( &lt;span class="Support"&gt;File&lt;/span&gt;.&lt;span class="Entity"&gt;dirname&lt;/span&gt;( strCacheFileName))
&lt;span class="line-numbers"&gt;  30 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  31 &lt;/span&gt;        &lt;span class="Support"&gt;ImageScience&lt;/span&gt;.&lt;span class="Entity"&gt;with_image&lt;/span&gt;( strFile) &lt;span class="Keyword"&gt;do &lt;/span&gt;|&lt;span class="Variable"&gt;oImg&lt;/span&gt;|
&lt;span class="line-numbers"&gt;  32 &lt;/span&gt;          oImg.&lt;span class="Entity"&gt;resize&lt;/span&gt;( nWidth, nWidth &lt;span class="Keyword"&gt;*&lt;/span&gt; oImg.&lt;span class="Entity"&gt;height&lt;/span&gt; &lt;span class="Keyword"&gt;/&lt;/span&gt; oImg.&lt;span class="Entity"&gt;width&lt;/span&gt;) &lt;span class="Keyword"&gt;do &lt;/span&gt;|&lt;span class="Variable"&gt;oResized&lt;/span&gt;|
&lt;span class="line-numbers"&gt;  33 &lt;/span&gt;            oResized.&lt;span class="Entity"&gt;save&lt;/span&gt; strCacheFileName
&lt;span class="line-numbers"&gt;  34 &lt;/span&gt;          &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  35 &lt;/span&gt;        &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  36 &lt;/span&gt;      &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  37 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  38 &lt;/span&gt;      &lt;span class="Keyword"&gt;return&lt;/span&gt; strCacheUrl
&lt;span class="line-numbers"&gt;  39 &lt;/span&gt;    &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  40 &lt;/span&gt; &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;
That wasn't too difficult. You need to create an 'imagecache' directory under /public to store a cache of resized images and make sure mongrel or whatever can write to it. 
&lt;/p&gt;
&lt;p&gt;
In a view one would put: 
&lt;/p&gt;
&lt;pre class="lazy"&gt;&lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;&lt;span class="Keyword"&gt;%=&lt;/span&gt; image_tag &lt;span class="Entity"&gt;cacheimage&lt;/span&gt;( &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;/images/prettypicture.jpg&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="Constant"&gt;500&lt;/span&gt;) &lt;span class="String"&gt;&lt;span class="String"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;
This will cause a version of prettypicture.jpg to be created that is resized to 500 pixels wide with a proportional change in height (in a typical html page layout the width of things is often more important than the height except maybe for headers and footers). The resized version is stored in a cache and will be displayed from then on. 
&lt;/p&gt;
&lt;p&gt;
If the original version of prettypicture.jpg is changed then one must remember to erase the resized versions from the cache as this isn't automatic. 
&lt;/p&gt;
&lt;p&gt;
Another helper is: 
&lt;/p&gt;
&lt;pre class="lazy"&gt;   &lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;applyimagewidth&lt;/span&gt;(&lt;span class="Variable"&gt; strText&lt;/span&gt;)
      strText.&lt;span class="Entity"&gt;gsub!&lt;/span&gt;( &lt;span class="String"&gt;&lt;span class="String"&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;(&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;[&lt;/span&gt;^&amp;quot;!&amp;lt;&amp;gt;{}&lt;span class="String"&gt;]&lt;/span&gt;&lt;/span&gt;*&lt;span class="String"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;(&lt;/span&gt;jpg|JPG|gif|GIF|png|PNG&lt;span class="String"&gt;)&lt;/span&gt;&lt;/span&gt;x&lt;span class="String"&gt;&lt;span class="String"&gt;(&lt;/span&gt;&lt;span class="Constant"&gt;\d&lt;/span&gt;+&lt;span class="String"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;/&lt;/span&gt;&lt;/span&gt;) &lt;span class="Keyword"&gt;do &lt;/span&gt;|&lt;span class="Variable"&gt;oMatch&lt;/span&gt;|
         &lt;span class="Entity"&gt;cacheimage&lt;/span&gt;( &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;&lt;span class="Variable"&gt;&lt;span class="Variable"&gt;$&lt;/span&gt;1&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;&lt;span class="Variable"&gt;&lt;span class="Variable"&gt;$&lt;/span&gt;2&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;$&lt;/span&gt;3&lt;/span&gt;.&lt;span class="Entity"&gt;to_i&lt;/span&gt;)
      &lt;span class="Keyword"&gt;end&lt;/span&gt;

      &lt;span class="Keyword"&gt;return&lt;/span&gt; strText
   &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;
(UPDATE: warning- the gsub above is VERY SLOW, it can take minutes to scan a few k of text, even if there are no matches). 
&lt;/p&gt;
&lt;p&gt;
which allows the width to be appended to the image file name. This allows a view such as 
&lt;/p&gt;
&lt;pre class="lazy"&gt;&lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;&lt;span class="Keyword"&gt;%=&lt;/span&gt; textilize &lt;span class="Entity"&gt;applyimagewidth&lt;/span&gt;( &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;page&lt;/span&gt;.&lt;span class="Entity"&gt;body&lt;/span&gt;) &lt;span class="String"&gt;&lt;span class="String"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;
and thus one's textile source could be: 
&lt;/p&gt;
&lt;pre class="lazy"&gt;!/imagecache/images/DSC_4064-300.JPG!
&lt;/pre&gt;
&lt;p&gt;
to show an image with a width of 300 pixels. I could hack the &lt;a href="http://whytheluckystiff.net/ruby/redcloth/"&gt;redcloth&lt;/a&gt; source directly to add this feature but I'd rather keep out of there. 
&lt;/p&gt;
&lt;p&gt;
I've done this in a simplistic hacky way but it should suffice (like all my stuff). 
&lt;/p&gt;&lt;p&gt;Related Posts: &lt;a href="/tag/rails"&gt;rails&lt;/a&gt; &lt;a href="/tag/ruby"&gt;ruby&lt;/a&gt;&lt;/p&gt;</description>
      <guid>http://www.petersblog.org/node/view/1658</guid>
      <category domain="http://www.technorati.com/tag">rails</category>
      <category domain="http://www.technorati.com/tag">ruby</category>
    </item>
    <item>
      <title>What's in my Pockets?</title>
      <link>http://www.petersblog.org/node/view/1645</link>
      <description>&lt;p&gt;
Another fusion of &lt;a href="/tag/wow"&gt;WoW&lt;/a&gt; and &lt;a href="/tag/rails"&gt;rails&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;
While planning Maexyn's conversion to alchemy I thought it would be nice to be able to get a listing of my characters bag and bank items so I could look through my stocks and see what was what. It occurred to me that a nice way to do this would be to scan through the lua files that are dumped by the TBags mod since that holds a database of contents of all my characters bags and bank. Doing this in &lt;a href="/tag/ruby"&gt;ruby&lt;/a&gt; turned out to be very easy: 
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;
Install ruby lua support: 
&lt;pre class="lazy"&gt;sudo aptitude install liblua5.1-0-dev
sudo gem install rua
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
Ruby code to convert lua data file into ruby data: 
&lt;pre class="lazy"&gt;oRua &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Support"&gt;Rua&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt;(&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;all&lt;/span&gt;)
oRua.&lt;span class="Entity"&gt;eval&lt;/span&gt;( &lt;span class="Entity"&gt;open&lt;/span&gt;( &lt;span class="Support"&gt;File&lt;/span&gt;.&lt;span class="Entity"&gt;dirname&lt;/span&gt;( &lt;span class="Variable"&gt;__FILE__&lt;/span&gt;) &lt;span class="Keyword"&gt;+&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;/../../db/TBag.lua&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;).&lt;span class="Entity"&gt;read&lt;/span&gt;)
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
oRua now has an array of items that maps onto the data stored in the lua file. 
&lt;pre class="lazy"&gt;oRua[&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;TBnkItm&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;] has a list of items &lt;span class="Keyword"&gt;in&lt;/span&gt; the bank
oRua[&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;TInvItm&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;] has a list of items &lt;span class="Keyword"&gt;in&lt;/span&gt; the inventory
&lt;/pre&gt;
&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;
The end result is &lt;a href="/post/wowbags"&gt;this page&lt;/a&gt; where I can see what stocks I have. Auctioneer also stores its data in lua so in a similar way I added a lookup so I see the values to these stocks. I can also use the item numbers to add links to the items to thottbot for reference. It isn't perfect, it should show vendor values, especially for soulbound items. 
&lt;/p&gt;
&lt;p&gt;
Anyway with this I can plan some sales, particularly of Maexyn's enchanting materials. 
&lt;/p&gt;
&lt;p&gt;
Currently the auctioneer data is way out of date, I need to get it running again and do a few scans. 
&lt;/p&gt;
&lt;p&gt;
This isn't here as a way of  boasting about my vast wealth, I guess a week's karak farming would be better that this junk pile (minions scourgestones?!?). 
&lt;/p&gt;
&lt;p&gt;
N.B: an alternative implementation would be simply to write something in the &lt;a href="http://www.lua.org"&gt;lua language&lt;/a&gt; that reads the two lua databases and then dumps out the html. For me it is easier to do it in ruby than learn lua. 
&lt;/p&gt;&lt;p&gt;Related Posts: &lt;a href="/tag/rails"&gt;rails&lt;/a&gt; &lt;a href="/tag/wow"&gt;wow&lt;/a&gt;&lt;/p&gt;</description>
      <guid>http://www.petersblog.org/node/view/1645</guid>
      <category domain="http://www.technorati.com/tag">rails</category>
      <category domain="http://www.technorati.com/tag">wow</category>
    </item>
    <item>
      <title>rspec</title>
      <link>http://www.petersblog.org/node/view/1630</link>
      <description>&lt;p&gt;
I've been trying out &lt;a href="http://rspec.info/"&gt;rspec&lt;/a&gt;, a new testing framework for &lt;a href="/tag/ruby"&gt;ruby&lt;/a&gt; that allows one to better follow the principles of Behaviour Driven Design (BDD). BDD is a step on from Test Driven Design (TDD) approach exemplified in the standard ruby unit test framework. TDD is the process of starting ones development by writing the tests that your new module should pass. As you write each test you then implement the code to pass it and hence, voila, you have nicely designed and robust software. In BDD you approach testing the code from how it is supposed to behave. When liasing with clients this is a better approach as everyone can talk in the same language, essentially in terms of requirements so one can try to get less bogged down in implementation details. 
&lt;/p&gt;
&lt;p&gt;
Here are some rspec tests for a class I have been working on: 
&lt;/p&gt;
&lt;pre class="lazy"&gt;&lt;span class="line-numbers"&gt;   1 &lt;/span&gt; describe &lt;span class="Variable"&gt;ReportHelper&lt;/span&gt; &lt;span class="Keyword"&gt;do&lt;/span&gt;
&lt;span class="line-numbers"&gt;   2 &lt;/span&gt;   &lt;span class="Entity"&gt;before&lt;/span&gt;(&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;each&lt;/span&gt;) &lt;span class="Keyword"&gt;do&lt;/span&gt;
&lt;span class="line-numbers"&gt;   3 &lt;/span&gt; &lt;span class="Comment"&gt;    &lt;span class="Comment"&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   4 &lt;/span&gt; &lt;span class="Comment"&gt;    &lt;span class="Comment"&gt;#&lt;/span&gt; Create a new clean Report class for each individual test&lt;/span&gt;
&lt;span class="line-numbers"&gt;   5 &lt;/span&gt; &lt;span class="Comment"&gt;    &lt;span class="Comment"&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   6 &lt;/span&gt;     &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;oReport&lt;/span&gt; &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Support"&gt;ReportHelper&lt;/span&gt;::&lt;span class="Entity"&gt;Report&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt;
&lt;span class="line-numbers"&gt;   7 &lt;/span&gt;   &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;   8 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;   9 &lt;/span&gt;   it &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;should allow a column to be added&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="Keyword"&gt;do&lt;/span&gt;
&lt;span class="line-numbers"&gt;  10 &lt;/span&gt;     &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;oReport&lt;/span&gt;.&lt;span class="Entity"&gt;AddColumn&lt;/span&gt;( &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;First Column&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;)
&lt;span class="line-numbers"&gt;  11 &lt;/span&gt;   &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  12 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  13 &lt;/span&gt;   it &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;should allow multiple columns to be added&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="Keyword"&gt;do&lt;/span&gt;
&lt;span class="line-numbers"&gt;  14 &lt;/span&gt;     &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;oReport&lt;/span&gt;.&lt;span class="Entity"&gt;AddColumn&lt;/span&gt;( &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;First Column&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;)
&lt;span class="line-numbers"&gt;  15 &lt;/span&gt;     &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;oReport&lt;/span&gt;.&lt;span class="Entity"&gt;AddColumn&lt;/span&gt;( &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;Second Column&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;)
&lt;span class="line-numbers"&gt;  16 &lt;/span&gt;   &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  17 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  18 &lt;/span&gt;   it &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;should allow rows to be added to the report&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="Keyword"&gt;do&lt;/span&gt;
&lt;span class="line-numbers"&gt;  19 &lt;/span&gt;     &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;oReport&lt;/span&gt;.&lt;span class="Entity"&gt;AddColumn&lt;/span&gt;( &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;First Column&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;)
&lt;span class="line-numbers"&gt;  20 &lt;/span&gt;     &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;oReport&lt;/span&gt;.&lt;span class="Entity"&gt;AddColumn&lt;/span&gt;( &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;Second Column&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;)
&lt;span class="line-numbers"&gt;  21 &lt;/span&gt;     &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;oReport&lt;/span&gt;.&lt;span class="Entity"&gt;AddRow&lt;/span&gt; &lt;span class="Keyword"&gt;do&lt;/span&gt;
&lt;span class="line-numbers"&gt;  22 &lt;/span&gt; &lt;span class="Comment"&gt;      &lt;span class="Comment"&gt;#&lt;/span&gt; pass&lt;/span&gt;
&lt;span class="line-numbers"&gt;  23 &lt;/span&gt;     &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  24 &lt;/span&gt;   &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  25 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  26 &lt;/span&gt;   it &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;should only allow rows to be added if columns exist&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="Keyword"&gt;do&lt;/span&gt;
&lt;span class="line-numbers"&gt;  27 &lt;/span&gt;     lambda{ &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;oReport&lt;/span&gt;.&lt;span class="Entity"&gt;AddRow&lt;/span&gt;}.&lt;span class="Entity"&gt;should&lt;/span&gt; &lt;span class="Entity"&gt;raise_error&lt;/span&gt;(&lt;span class="Variable"&gt;RuntimeError&lt;/span&gt;, &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;No columns defined&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;)
&lt;span class="line-numbers"&gt;  28 &lt;/span&gt;   &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  29 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  30 &lt;/span&gt;   it &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;should return simple cell values&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="Keyword"&gt;do&lt;/span&gt;
&lt;span class="line-numbers"&gt;  31 &lt;/span&gt;     &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;oReport&lt;/span&gt;.&lt;span class="Entity"&gt;AddColumn&lt;/span&gt;( &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;First Column&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;)
&lt;span class="line-numbers"&gt;  32 &lt;/span&gt;     &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;oReport&lt;/span&gt;.&lt;span class="Entity"&gt;AddColumn&lt;/span&gt;( &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;Second Column&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;)
&lt;span class="line-numbers"&gt;  33 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  34 &lt;/span&gt;     &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;oReport&lt;/span&gt;.&lt;span class="Entity"&gt;AddRow&lt;/span&gt; &lt;span class="Keyword"&gt;do &lt;/span&gt;|&lt;span class="Variable"&gt;oRow&lt;/span&gt;|
&lt;span class="line-numbers"&gt;  35 &lt;/span&gt;       oRow[ &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;First Column&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;] &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Constant"&gt;98&lt;/span&gt;
&lt;span class="line-numbers"&gt;  36 &lt;/span&gt;       oRow[ &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;Second Column&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;] &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Constant"&gt;198&lt;/span&gt;
&lt;span class="line-numbers"&gt;  37 &lt;/span&gt;     &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  38 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  39 &lt;/span&gt;     &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;oReport&lt;/span&gt;[&lt;span class="Constant"&gt;0&lt;/span&gt;][&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;First Column&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;].&lt;span class="Entity"&gt;should&lt;/span&gt; &lt;span class="Keyword"&gt;==&lt;/span&gt; &lt;span class="Constant"&gt;98&lt;/span&gt;
&lt;span class="line-numbers"&gt;  40 &lt;/span&gt;     &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;oReport&lt;/span&gt;[&lt;span class="Constant"&gt;0&lt;/span&gt;][&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;Second Column&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;].&lt;span class="Entity"&gt;should&lt;/span&gt; &lt;span class="Keyword"&gt;==&lt;/span&gt; &lt;span class="Constant"&gt;198&lt;/span&gt;
&lt;span class="line-numbers"&gt;  41 &lt;/span&gt;   &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  42 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  43 &lt;/span&gt; &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;
Each test has a descriptive string that can be interpreted in two ways: as a test that must be passed or as a requirement for the software. The rspec toolset includes something that will pull out all these strings and give a summary of what testing has been done/what the requirements for the software are. If an auditor asks how the software is tested, bang, there is a full report of the tests. At a practical level (i.e. makes life easier for me) I prefer this to the ruby unit test system where one has to think up meaningful_method_names for each test. The strings are easier to write and read and reduce the need for comments. 
&lt;/p&gt;
&lt;p&gt;
Example report: 
&lt;/p&gt;
&lt;div class="verbatim-block"&gt;&lt;pre&gt;ReportHelper

- should allow a column to be added
- should allow multiple columns to be added
- should allow rows to be added to the report
- should only allow rows to be added if columns exist
- should return simple cell values
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;
Rspec contains sweeter syntactic sugar than the ruby unit test module. For example, rspec overloads every object in the system with a 'should' method for doing the equivalent of assert tests: 
&lt;/p&gt;
&lt;pre class="lazy"&gt;    &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;oReport&lt;/span&gt;[&lt;span class="Constant"&gt;0&lt;/span&gt;][&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;First Column&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;].&lt;span class="Entity"&gt;should&lt;/span&gt; &lt;span class="Keyword"&gt;==&lt;/span&gt; &lt;span class="Constant"&gt;98&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;
Compare this to test/unit: 
&lt;/p&gt;
&lt;pre class="lazy"&gt;    assert_equal &lt;span class="Constant"&gt;98&lt;/span&gt;, &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;oReport&lt;/span&gt;[&lt;span class="Constant"&gt;0&lt;/span&gt;][&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;First Column&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;]
&lt;/pre&gt;
&lt;p&gt;
apart from more typing, assert_equal has the expectation/actual things in what is intuitively to me the wrong order and I'm always having to correct these lines because the error messages have the terms swapped. 
&lt;/p&gt;
&lt;p&gt;
Rspec can test assertions more cleanly: 
&lt;/p&gt;
&lt;pre class="lazy"&gt;    lambda{ &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;oReport&lt;/span&gt;.&lt;span class="Entity"&gt;AddRow&lt;/span&gt;}.&lt;span class="Entity"&gt;should&lt;/span&gt; &lt;span class="Entity"&gt;raise_error&lt;/span&gt;(&lt;span class="Variable"&gt;RuntimeError&lt;/span&gt;, &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;No columns defined&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;)
&lt;/pre&gt;
&lt;p&gt;
although it's a pity that the lambda is necessary (and probably no 1 noob trap) but with test/unit the following always seemed like a lot of typing: 
&lt;/p&gt;
&lt;pre class="lazy"&gt;    oError &lt;span class="Keyword"&gt;=&lt;/span&gt; assert_raises &lt;span class="Variable"&gt;RuntimeError&lt;/span&gt; &lt;span class="Keyword"&gt;do&lt;/span&gt;
      &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;oReport&lt;/span&gt;.&lt;span class="Entity"&gt;AddRow&lt;/span&gt;
    &lt;span class="Keyword"&gt;end&lt;/span&gt;
    assert_equal &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;No columns defined&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;, oError.&lt;span class="Entity"&gt;message&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;
rspec is nicely integrated into &lt;a href="/tag/rails"&gt;rails&lt;/a&gt;, plugins being available to generate rspec tested controllers and models and suchlike, rake tasks to generate documents (woohoo) etc. It is integrated into &lt;a href="/tag/netbeans"&gt;netbeans&lt;/a&gt; and runs ok on Windows although when the tests are run (CTRL-F6) there is a tedious few seconds wasted in startup time. It is possible to run an rspec server in the background to eliminate this startup but it doesn't work on windows native ruby, it gives an error lamenting Microsofts glorious omission of a fork api. 
&lt;/p&gt;
&lt;p&gt;
There is much more to Rspec than this. One aim seems to be to write psuedo testing languages and have clients use these to write stories about what the software will do, these stories turning into a test framework. As the slashdot tag goes, 'goodluckwiththat'. At a practical level, I'm liking rspec even if only as a unit test framework with added syntactic sugar. 
&lt;/p&gt;&lt;p&gt;Related Posts: &lt;a href="/tag/netbeans"&gt;netbeans&lt;/a&gt; &lt;a href="/tag/rails"&gt;rails&lt;/a&gt; &lt;a href="/tag/rspec"&gt;rspec&lt;/a&gt; &lt;a href="/tag/ruby"&gt;ruby&lt;/a&gt; &lt;a href="/tag/testing"&gt;testing&lt;/a&gt;&lt;/p&gt;</description>
      <guid>http://www.petersblog.org/node/view/1630</guid>
      <category domain="http://www.technorati.com/tag">netbeans</category>
      <category domain="http://www.technorati.com/tag">rails</category>
      <category domain="http://www.technorati.com/tag">rspec</category>
      <category domain="http://www.technorati.com/tag">ruby</category>
      <category domain="http://www.technorati.com/tag">testing</category>
    </item>
    <item>
      <title>Illustrated Blog</title>
      <link>http://www.petersblog.org/node/view/1620</link>
      <description>&lt;p&gt;
Up till now to put pictures in this blog I have had to: 
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;
go into an image editor to resize the file to suit the blog (about 450-500 pixels wide). 
&lt;/li&gt;
&lt;li&gt;
fire up an sftp program to upload the file to /var/www/PetersBlogger/public/images 
&lt;/li&gt;
&lt;li&gt;
add a tag such as 
&lt;div class="verbatim-block"&gt;&lt;pre&gt;[/images/TheNewPicture.jpg]
&lt;/pre&gt;&lt;/div&gt;
to the &lt;a href="/tag/wilki"&gt;wilki&lt;/a&gt;. 
&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;
It was a bit laborious, especially the SFTP bit which is why I didn't do it very often. 
&lt;/p&gt;
&lt;p&gt;
I've now added better support for pictures to &lt;a href="/tag/petersblogger"&gt;PetersBlogger&lt;/a&gt;. Here is an example: 
&lt;/p&gt;
&lt;div class="picture"&gt;&lt;div class="pictureframe"&gt;&lt;a href="/pictures/0000/0004/DSC00015.jpg" rel="lightbox" title="Girls Aloud"&gt;&lt;img alt="Girls Aloud" src="/pictures/0000/0004/DSC00015_normal.jpg"&gt;&lt;/a&gt;&lt;br/&gt;&lt;p&gt;Girls Aloud&lt;/p&gt;&lt;/div&gt;&lt;/div&gt; 
&lt;p&gt;
if you click on this you should be treated to a larger version of the image, thanks to &lt;a href="http://www.huddletogether.com/projects/lightbox2/"&gt;lightbox&lt;/a&gt; a javascript library. 
&lt;/p&gt;
&lt;p&gt;
PetersBlogger now supports a database of pictures so I can add a new picture, give it a name and upload it through the administration pages. The code uses the name as a caption under the photo and as alt text. I can use the name directly in my wilki text to choose which pictures to display. 
&lt;/p&gt;
&lt;p&gt;
The database of pictures is handled by the &lt;a href="http://agilewebdevelopment.com/plugins/attachment_fu"&gt;attachmentfu&lt;/a&gt; &lt;a href="/tag/rails"&gt;rails&lt;/a&gt; plugin. &lt;a href="http://clarkware.com/cgi/blosxom/2007/02/24#FileUploadFu"&gt;This article&lt;/a&gt; gives the steps required to get attachmentfu up and running. 
&lt;/p&gt;
&lt;p&gt;
The setup automatically handles resizing images to three sizes: thumbnail for listing in my picture admin page, a version that is no more than 500 pixels in either dimension for showing in the blog and a version that is no more than 800x600 for showing zoomed when you click on a picture. Attachmentfu has the sense to keep the aspect ratio correct automatically so I don't have to worry about portrait/landscape. I had to hack the imagescience library that does the resizing as it was hard-wired to save jpegs using JPEG_QUALITYSUPERB mode so the blog-sized images were taking up 150k. I changed this to JPEG_QUALITYAVERAGE and the size dropped to 15k or so, but it was a bit noisy so I've settled on JPEG_QUALITYGOOD and 38k images. You are on broadband? 
&lt;/p&gt;
&lt;p&gt;
I was slightly worried about how to change pictures, i.e. to upload a different version of a picture to replace an existing one, since the attachmentfu article I mentioned does not cover this. The answer is to use the exact same view code in the 'edit' page as in the 'new' page. Combined with rails scaffold code it Just Works! It even handles deleting the three old pictures and creating the new files. 
&lt;/p&gt;
&lt;p&gt;
My main motivation for doing this was so I can do it on the websites I host for other people and make it easier for them to upload pictures. PetersCMS is still in the planning stages but one problem has been cracked. 
&lt;/p&gt;&lt;p&gt;Related Posts: &lt;a href="/tag/petersblogger"&gt;petersblogger&lt;/a&gt; &lt;a href="/tag/rails"&gt;rails&lt;/a&gt;&lt;/p&gt;</description>
      <guid>http://www.petersblog.org/node/view/1620</guid>
      <category domain="http://www.technorati.com/tag">petersblogger</category>
      <category domain="http://www.technorati.com/tag">rails</category>
    </item>
    <item>
      <title>Embedding Flash Player</title>
      <link>http://www.petersblog.org/node/view/1614</link>
      <description>&lt;p&gt;
Much as I hate flash, it is a nice way to embed a sound or video player in a page. Here's how I did it in &lt;a href="/tag/rails"&gt;rails&lt;/a&gt; using the &lt;a href="http://www.jeroenwijering.com/?item=JW_FLV_Player"&gt;JW FLV Media Player&lt;/a&gt;. I added the following to my view: 
&lt;/p&gt;
&lt;pre class="lazy"&gt;&lt;span class="line-numbers"&gt;   1 &lt;/span&gt; &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;&lt;/span&gt;&lt;span class="MetaTag"&gt;script&lt;/span&gt; &lt;span class="MetaTag"&gt;type&lt;/span&gt;=&lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;text/javascript&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="MetaTag"&gt;src&lt;/span&gt;=&lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;/javascripts/swfobject.js&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;span class="MetaTag"&gt;&amp;lt;&lt;/span&gt;/&lt;/span&gt;&lt;span class="MetaTag"&gt;script&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   2 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;   3 &lt;/span&gt; &amp;lt;% &lt;span class="Keyword"&gt;for&lt;/span&gt; oRssItem &lt;span class="Keyword"&gt;in&lt;/span&gt; &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;rssitems&lt;/span&gt; %&amp;gt;
&lt;span class="line-numbers"&gt;   4 &lt;/span&gt;   &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;&lt;/span&gt;&lt;span class="MetaTag"&gt;hr&lt;/span&gt;/&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   5 &lt;/span&gt;   &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;&lt;/span&gt;&lt;span class="MetaTag"&gt;h3&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&amp;lt;%= oRssItem.&lt;span class="Entity"&gt;title&lt;/span&gt; %&amp;gt;&lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="MetaTag"&gt;h3&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   6 &lt;/span&gt;   &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;&lt;/span&gt;&lt;span class="MetaTag"&gt;p&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&amp;lt;%= oRssItem.&lt;span class="Entity"&gt;description&lt;/span&gt; %&amp;gt;&lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="MetaTag"&gt;p&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   7 &lt;/span&gt;   &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;&lt;/span&gt;&lt;span class="MetaTag"&gt;table&lt;/span&gt; &lt;span class="MetaTag"&gt;width&lt;/span&gt;=&lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;470&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   8 &lt;/span&gt;     &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;&lt;/span&gt;&lt;span class="MetaTag"&gt;tr&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   9 &lt;/span&gt;        &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;&lt;/span&gt;&lt;span class="MetaTag"&gt;td&lt;/span&gt; &lt;span class="MetaTag"&gt;wIdth&lt;/span&gt;=&lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;50%&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&amp;lt;%= oRssItem.&lt;span class="Entity"&gt;created&lt;/span&gt;.&lt;span class="Entity"&gt;strftime&lt;/span&gt;( &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;%m %B %y&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;) %&amp;gt;&lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="MetaTag"&gt;td&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  10 &lt;/span&gt;        &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;&lt;/span&gt;&lt;span class="MetaTag"&gt;td&lt;/span&gt; &lt;span class="MetaTag"&gt;align&lt;/span&gt;=&lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;right&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&amp;lt;%= link_to &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;Download&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;/&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;&lt;span class="String"&gt; &lt;/span&gt;oRssItem&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;filename&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; %&amp;gt;&lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="MetaTag"&gt;td&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  11 &lt;/span&gt;     &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="MetaTag"&gt;tr&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  12 &lt;/span&gt;   &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="MetaTag"&gt;table&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  13 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  14 &lt;/span&gt;   &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;&lt;/span&gt;&lt;span class="MetaTag"&gt;div&lt;/span&gt; &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;id&lt;/span&gt;&lt;span class="MetaTag"&gt;=&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;span class="String"&gt;player&lt;span class="String"&gt;&lt;span class="String"&gt;&amp;lt;%=&lt;/span&gt;oRssItem&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="MetaTag"&gt;id&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;This text will be replaced&lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="MetaTag"&gt;div&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  15 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  16 &lt;/span&gt;   &amp;lt;&lt;span class="Entity"&gt;script&lt;/span&gt; &lt;span class="Entity"&gt;type&lt;/span&gt;=&lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;text/javascript&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&amp;gt;
&lt;span class="line-numbers"&gt;  17 &lt;/span&gt;     &lt;span class="Keyword"&gt;var&lt;/span&gt; so &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Keyword"&gt;new&lt;/span&gt; &lt;span class="Entity"&gt;SWFObject&lt;/span&gt;(&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;/mediaplayer.swf&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;,&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;mpl&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;,&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;470&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;,&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;20&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;,&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;8&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;);
&lt;span class="line-numbers"&gt;  18 &lt;/span&gt;     so.addParam(&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;allowscriptaccess&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;,&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;always&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;);
&lt;span class="line-numbers"&gt;  19 &lt;/span&gt;     so.addParam(&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;allowfullscreen&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;,&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;true&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;);
&lt;span class="line-numbers"&gt;  20 &lt;/span&gt;     so.addVariable(&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;width&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;,&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;470&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;);
&lt;span class="line-numbers"&gt;  21 &lt;/span&gt;     so.addVariable(&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;height&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;,&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;20&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;);
&lt;span class="line-numbers"&gt;  22 &lt;/span&gt;     so.addVariable(&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;file&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;,&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;/&amp;lt;%= oRssItem.filename%&amp;gt;&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;);
&lt;span class="line-numbers"&gt;  23 &lt;/span&gt;     so.&lt;span class="Support"&gt;write&lt;/span&gt;(&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;player&amp;lt;%=oRssItem.id%&amp;gt;&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;);
&lt;span class="line-numbers"&gt;  24 &lt;/span&gt;   &amp;lt;/&lt;span class="Entity"&gt;script&lt;/span&gt;&amp;gt;
&lt;span class="line-numbers"&gt;  25 &lt;/span&gt; &amp;lt;% &lt;span class="Keyword"&gt;end&lt;/span&gt; %&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
This generates a page full of podcast entries, each with a little media player thing so you can listen to them easily. This does mp3 files but it can handle video too. 
&lt;/p&gt;
&lt;p&gt;
I am aware that the swfobject.js would be better loaded in the html header. 
&lt;/p&gt;
&lt;p&gt;
Here's a demo: I don't know if this will work in the rss feed (UPDATE: no!) so bear with me and please be gentle on my bandwidth, it's quite a catchy tune: 
&lt;/p&gt;
&lt;p&gt;
&lt;script type="text/javascript" src="/javascripts/swfobject.js"&gt;&lt;/script&gt; 
&lt;/p&gt;
&lt;div class="rawhtml-block"&gt;  &lt;div id="player571"&gt;Oops, no flash? Javascript broken? Works for me.&lt;/div&gt;

  &lt;script type="text/javascript"&gt;
    var so = new SWFObject('/mediaplayer.swf','mpl','470','20','8');
    so.addParam('allowscriptaccess','always');
    so.addParam('allowfullscreen','true');
    so.addVariable('width','470');
    so.addVariable('height','20');
    so.addVariable('file','/files/um_bongo_lo.mp3');
    so.write('player571');
  &lt;/script&gt;
&lt;/div&gt;
&lt;p&gt;
The main reason I am blogging this is because it only needs a change to a view file and installation of two files in the /public directory (the player and a javascript file). Since view files are awkward to comment legibly I decided to document it all here so I can find it again. 
&lt;/p&gt;&lt;p&gt;Related Posts: &lt;a href="/tag/mp3"&gt;mp3&lt;/a&gt; &lt;a href="/tag/rails"&gt;rails&lt;/a&gt; &lt;a href="/tag/ruby"&gt;ruby&lt;/a&gt;&lt;/p&gt;</description>
      <guid>http://www.petersblog.org/node/view/1614</guid>
      <category domain="http://www.technorati.com/tag">mp3</category>
      <category domain="http://www.technorati.com/tag">rails</category>
      <category domain="http://www.technorati.com/tag">ruby</category>
    </item>
    <item>
      <title>Smile</title>
      <link>http://www.petersblog.org/node/view/1600</link>
      <description>&lt;p&gt;
In a mammoth programming session I implemented proper smileys &lt;img alt="wink" src="/images/smileys/wink.png" /&gt; 
&lt;/p&gt;
&lt;pre class="lazy"&gt;[[&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;:-)&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;smile&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;],
  [&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;:-(&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;sad&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;],
  [&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;8-)&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;cool&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;],
  [&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;;-)&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;wink&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;]].&lt;span class="Entity"&gt;each&lt;/span&gt; &lt;span class="Keyword"&gt;do &lt;/span&gt;|&lt;span class="Variable"&gt;strCode&lt;/span&gt;, &lt;span class="Variable"&gt;strFile&lt;/span&gt;|
  strText.&lt;span class="Entity"&gt;gsub!&lt;/span&gt;( strCode, &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&amp;lt;img alt=&lt;span class="Constant"&gt;\&amp;quot;&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;strFile&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="Constant"&gt;\&amp;quot;&lt;/span&gt; src=&lt;span class="Constant"&gt;\&amp;quot;&lt;/span&gt;/images/smileys/&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;strFile&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;.png&lt;span class="Constant"&gt;\&amp;quot;&lt;/span&gt; /&amp;gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)
&lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;
My fingers were bleeding from all the typing &lt;img alt="sad" src="/images/smileys/sad.png" /&gt; Worked first time though &lt;img alt="smile" src="/images/smileys/smile.png" /&gt; 
&lt;/p&gt;
&lt;p&gt;
There is even alt text for blind people &lt;img alt="cool" src="/images/smileys/cool.png" /&gt; 
&lt;/p&gt;&lt;p&gt;Related Posts: &lt;a href="/tag/petersblogger"&gt;petersblogger&lt;/a&gt; &lt;a href="/tag/rails"&gt;rails&lt;/a&gt; &lt;a href="/tag/ruby"&gt;ruby&lt;/a&gt;&lt;/p&gt;</description>
      <guid>http://www.petersblog.org/node/view/1600</guid>
      <category domain="http://www.technorati.com/tag">petersblogger</category>
      <category domain="http://www.technorati.com/tag">rails</category>
      <category domain="http://www.technorati.com/tag">ruby</category>
    </item>
    <item>
      <title>Scraping WoW Armory in Ruby</title>
      <link>http://www.petersblog.org/node/view/1594</link>
      <description>&lt;p&gt;
I figured out how to scrape the Wow Armory for up-to-date information about my characters using ruby. This means I can do this directly in &lt;a href="/tag/petersblogger"&gt;PetersBlogger&lt;/a&gt; without using third party signature generators that disappear overnight or get absorbed into wow gold sites. 
&lt;/p&gt;
&lt;pre class="lazy"&gt;&lt;span class="line-numbers"&gt;   1 &lt;/span&gt;   &lt;span class="Keyword"&gt;require&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;net/http&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   2 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;   3 &lt;/span&gt; &lt;span class="Comment"&gt;  &lt;span class="Comment"&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   4 &lt;/span&gt; &lt;span class="Comment"&gt;  &lt;span class="Comment"&gt;#&lt;/span&gt; Scrape wow character info.&lt;/span&gt;
&lt;span class="line-numbers"&gt;   5 &lt;/span&gt; &lt;span class="Comment"&gt;  &lt;span class="Comment"&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   6 &lt;/span&gt;   &lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;WoWInfo&lt;/span&gt;
&lt;span class="line-numbers"&gt;   7 &lt;/span&gt;     &lt;span class="Keyword"&gt;begin&lt;/span&gt;
&lt;span class="line-numbers"&gt;   8 &lt;/span&gt;       strRealm &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;Eonar&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   9 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  10 &lt;/span&gt;       oInfo &lt;span class="Keyword"&gt;=&lt;/span&gt; []
&lt;span class="line-numbers"&gt;  11 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  12 &lt;/span&gt;       [&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;Pookypoo&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;Maevyn&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;Maezyn&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;Maexyn&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;].&lt;span class="Entity"&gt;each&lt;/span&gt; &lt;span class="Keyword"&gt;do &lt;/span&gt;|&lt;span class="Variable"&gt;strCharacter&lt;/span&gt;|
&lt;span class="line-numbers"&gt;  13 &lt;/span&gt;         oCharInfo &lt;span class="Keyword"&gt;=&lt;/span&gt; []
&lt;span class="line-numbers"&gt;  14 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  15 &lt;/span&gt; &lt;span class="Comment"&gt;        &lt;span class="Comment"&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  16 &lt;/span&gt; &lt;span class="Comment"&gt;        &lt;span class="Comment"&gt;#&lt;/span&gt; Open url.&lt;/span&gt;
&lt;span class="line-numbers"&gt;  17 &lt;/span&gt; &lt;span class="Comment"&gt;        &lt;span class="Comment"&gt;#&lt;/span&gt; Need to specify firefox as user agent as this makes the server return an XML&lt;/span&gt;
&lt;span class="line-numbers"&gt;  18 &lt;/span&gt; &lt;span class="Comment"&gt;        &lt;span class="Comment"&gt;#&lt;/span&gt; file.&lt;/span&gt;
&lt;span class="line-numbers"&gt;  19 &lt;/span&gt; &lt;span class="Comment"&gt;        &lt;span class="Comment"&gt;#&lt;/span&gt; Look the data up in the european armory. Change this for US.&lt;/span&gt;
&lt;span class="line-numbers"&gt;  20 &lt;/span&gt; &lt;span class="Comment"&gt;        &lt;span class="Comment"&gt;#&lt;/span&gt; If this is not done we get html.&lt;/span&gt;
&lt;span class="line-numbers"&gt;  21 &lt;/span&gt;         oResp &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Support"&gt;Net&lt;/span&gt;::&lt;span class="Entity"&gt;HTTP&lt;/span&gt;.&lt;span class="Entity"&gt;start&lt;/span&gt;( &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;armory.wow-europe.com&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="Constant"&gt;80&lt;/span&gt;) &lt;span class="Keyword"&gt;do &lt;/span&gt;|&lt;span class="Variable"&gt;http&lt;/span&gt;|
&lt;span class="line-numbers"&gt;  22 &lt;/span&gt;               http.&lt;span class="Entity"&gt;get&lt;/span&gt;( &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;/character-sheet.xml?r=&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;strRealm&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;tr&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;(&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt; &lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;,&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;+&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;)&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;&amp;amp;n=&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;strCharacter&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;,
&lt;span class="line-numbers"&gt;  23 &lt;/span&gt;                           { &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;user-agent&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;Mozilla/5.0 (Windows; U; Windows NT 5.0; en-GB; rv:1.8.1.4) Gecko/20070515 Firefox/2.0.0.4&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;})
&lt;span class="line-numbers"&gt;  24 &lt;/span&gt;         &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  25 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  26 &lt;/span&gt;         oDoc &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Variable"&gt;REXML&lt;/span&gt;::&lt;span class="Entity"&gt;Document&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt; oResp.&lt;span class="Entity"&gt;body&lt;/span&gt;
&lt;span class="line-numbers"&gt;  27 &lt;/span&gt;         oDoc.&lt;span class="Entity"&gt;elements&lt;/span&gt;[&lt;span class="Constant"&gt;1&lt;/span&gt;].&lt;span class="Entity"&gt;elements&lt;/span&gt;.&lt;span class="Entity"&gt;each&lt;/span&gt;( &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;characterInfo/character&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;) &lt;span class="Keyword"&gt;do &lt;/span&gt;|&lt;span class="Variable"&gt;oElement&lt;/span&gt;|
&lt;span class="line-numbers"&gt;  28 &lt;/span&gt;           oCharInfo &lt;span class="Keyword"&gt;&amp;lt;&amp;lt;&lt;/span&gt; {&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;race&lt;/span&gt; =&amp;gt; oElement.&lt;span class="Entity"&gt;attributes&lt;/span&gt;[&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;race&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;]}
&lt;span class="line-numbers"&gt;  29 &lt;/span&gt;           oCharInfo &lt;span class="Keyword"&gt;&amp;lt;&amp;lt;&lt;/span&gt; {&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;class&lt;/span&gt; =&amp;gt; oElement.&lt;span class="Entity"&gt;attributes&lt;/span&gt;[&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;class&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;]}
&lt;span class="line-numbers"&gt;  30 &lt;/span&gt;           oCharInfo &lt;span class="Keyword"&gt;&amp;lt;&amp;lt;&lt;/span&gt; {&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;level&lt;/span&gt; =&amp;gt; oElement.&lt;span class="Entity"&gt;attributes&lt;/span&gt;[&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;level&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;]}
&lt;span class="line-numbers"&gt;  31 &lt;/span&gt;         &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  32 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  33 &lt;/span&gt;         oDoc.&lt;span class="Entity"&gt;elements&lt;/span&gt;[&lt;span class="Constant"&gt;1&lt;/span&gt;].&lt;span class="Entity"&gt;elements&lt;/span&gt;.&lt;span class="Entity"&gt;each&lt;/span&gt;( &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;characterInfo/characterTab/talentSpec&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;) &lt;span class="Keyword"&gt;do &lt;/span&gt;|&lt;span class="Variable"&gt;oElement&lt;/span&gt;|
&lt;span class="line-numbers"&gt;  34 &lt;/span&gt;           oCharInfo &lt;span class="Keyword"&gt;&amp;lt;&amp;lt;&lt;/span&gt; {&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;spec&lt;/span&gt; =&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;oElement&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;attributes&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;[&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;treeOne&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;]&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;/&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;oElement&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;attributes&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;[&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;treeTwo&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;]&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;/&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;oElement&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;attributes&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;[&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;treeThree&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;]&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;}
&lt;span class="line-numbers"&gt;  35 &lt;/span&gt;         &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  36 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  37 &lt;/span&gt;         oDoc.&lt;span class="Entity"&gt;elements&lt;/span&gt;[&lt;span class="Constant"&gt;1&lt;/span&gt;].&lt;span class="Entity"&gt;elements&lt;/span&gt;.&lt;span class="Entity"&gt;each&lt;/span&gt;( &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;characterInfo/characterTab/professions/skill&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;) &lt;span class="Keyword"&gt;do &lt;/span&gt;|&lt;span class="Variable"&gt;oElement&lt;/span&gt;|
&lt;span class="line-numbers"&gt;  38 &lt;/span&gt;           oCharInfo &lt;span class="Keyword"&gt;&amp;lt;&amp;lt;&lt;/span&gt; {oElement.&lt;span class="Entity"&gt;attributes&lt;/span&gt;[&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;key&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;].&lt;span class="Entity"&gt;to_sym&lt;/span&gt; =&amp;gt; oElement.&lt;span class="Entity"&gt;attributes&lt;/span&gt;[&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;value&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;]}
&lt;span class="line-numbers"&gt;  39 &lt;/span&gt;         &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  40 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  41 &lt;/span&gt;         oInfo &lt;span class="Keyword"&gt;&amp;lt;&amp;lt;&lt;/span&gt; {strCharacter =&amp;gt; oCharInfo}
&lt;span class="line-numbers"&gt;  42 &lt;/span&gt;       &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  43 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  44 &lt;/span&gt;       &lt;span class="Keyword"&gt;return&lt;/span&gt; oInfo
&lt;span class="line-numbers"&gt;  45 &lt;/span&gt;     &lt;span class="Keyword"&gt;rescue&lt;/span&gt;
&lt;span class="line-numbers"&gt;  46 &lt;/span&gt;       &lt;span class="Keyword"&gt;return&lt;/span&gt; []
&lt;span class="line-numbers"&gt;  47 &lt;/span&gt;     &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  48 &lt;/span&gt;   &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;
This lot generates the info in &lt;a href="/post/wow"&gt;this page&lt;/a&gt; which is linked to under 'My Characters' over to the right. It should be fairly up-to-date as the cache will flush once a day causing the armory to be requeried. Currently the output is presented in a rails view thus: 
&lt;/p&gt;
&lt;pre class="lazy"&gt;&lt;span class="line-numbers"&gt;   1 &lt;/span&gt; &amp;lt;% &lt;span class="Entity"&gt;cache&lt;/span&gt;( &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;part&lt;/span&gt; =&amp;gt; &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;wow&lt;/span&gt;) &lt;span class="Keyword"&gt;do &lt;/span&gt;%&amp;gt;
&lt;span class="line-numbers"&gt;   2 &lt;/span&gt;   &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;&lt;/span&gt;&lt;span class="MetaTag"&gt;table&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   3 &lt;/span&gt;   &amp;lt;% &lt;span class="Variable"&gt;WoWInfo&lt;/span&gt;().&lt;span class="Entity"&gt;each&lt;/span&gt; &lt;span class="Keyword"&gt;do &lt;/span&gt;|&lt;span class="Variable"&gt;oCharInfo&lt;/span&gt;| %&amp;gt;
&lt;span class="line-numbers"&gt;   4 &lt;/span&gt;     &amp;lt;% oCharInfo.&lt;span class="Entity"&gt;each_pair&lt;/span&gt; &lt;span class="Keyword"&gt;do &lt;/span&gt;|&lt;span class="Variable"&gt;strChar&lt;/span&gt;, &lt;span class="Variable"&gt;oInfo&lt;/span&gt;| %&amp;gt;
&lt;span class="line-numbers"&gt;   5 &lt;/span&gt;       &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;&lt;/span&gt;&lt;span class="MetaTag"&gt;tr&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;&lt;/span&gt;&lt;span class="MetaTag"&gt;td&lt;/span&gt; &lt;span class="MetaTag"&gt;colspan&lt;/span&gt;=&lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;2&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;&lt;/span&gt;&lt;span class="MetaTag"&gt;b&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&amp;lt;%= strChar %&amp;gt;&lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="MetaTag"&gt;b&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="MetaTag"&gt;td&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="MetaTag"&gt;tr&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   6 &lt;/span&gt;       &amp;lt;% oInfo.&lt;span class="Entity"&gt;each&lt;/span&gt; &lt;span class="Keyword"&gt;do &lt;/span&gt;|&lt;span class="Variable"&gt;oItem&lt;/span&gt;| %&amp;gt;
&lt;span class="line-numbers"&gt;   7 &lt;/span&gt;         &amp;lt;% oItem.&lt;span class="Entity"&gt;each_pair&lt;/span&gt; &lt;span class="Keyword"&gt;do &lt;/span&gt;|&lt;span class="Variable"&gt;strItem&lt;/span&gt;, &lt;span class="Variable"&gt;strValue&lt;/span&gt;| %&amp;gt;
&lt;span class="line-numbers"&gt;   8 &lt;/span&gt;           &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;&lt;/span&gt;&lt;span class="MetaTag"&gt;tr&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   9 &lt;/span&gt;             &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;&lt;/span&gt;&lt;span class="MetaTag"&gt;td&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&amp;lt;%= strItem %&amp;gt;:&lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="MetaTag"&gt;td&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;&lt;/span&gt;&lt;span class="MetaTag"&gt;td&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&amp;lt;%= strValue %&amp;gt;&lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="MetaTag"&gt;td&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  10 &lt;/span&gt;           &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="MetaTag"&gt;tr&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  11 &lt;/span&gt;         &amp;lt;% &lt;span class="Keyword"&gt;end&lt;/span&gt; %&amp;gt;
&lt;span class="line-numbers"&gt;  12 &lt;/span&gt;       &amp;lt;% &lt;span class="Keyword"&gt;end&lt;/span&gt; %&amp;gt;
&lt;span class="line-numbers"&gt;  13 &lt;/span&gt;     &amp;lt;% &lt;span class="Keyword"&gt;end&lt;/span&gt; %&amp;gt;
&lt;span class="line-numbers"&gt;  14 &lt;/span&gt;   &amp;lt;% &lt;span class="Keyword"&gt;end&lt;/span&gt; %&amp;gt;
&lt;span class="line-numbers"&gt;  15 &lt;/span&gt;   &lt;span class="MetaTag"&gt;&lt;span class="MetaTag"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="MetaTag"&gt;table&lt;/span&gt;&lt;span class="MetaTag"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  16 &lt;/span&gt; &amp;lt;% &lt;span class="Keyword"&gt;end&lt;/span&gt; %&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
All the work is done by the WoWInfo method which I put in the application helper. I didn't use a controller method for this. 
&lt;/p&gt;
&lt;p&gt;
I'm going to think about a nicer presentation with some pictures of the actual guys rather than generic pictures of bald redhead dwarves. 
&lt;/p&gt;
&lt;p&gt;
UPDATE: I've added my old Aerie Peak toons to the output but the code above still stands. &lt;a class="autolink" href="http://eu.wowarmory.com/character-sheet.xml?r=Aerie+Peak&amp;n=Lugulas" title="Lowbie Rogue"&gt;Lugulas&lt;/a&gt; was my old auction alt who I've never named here before, hence level 10 with level 75 (dis)enchanting. I find myself missing them... 
&lt;/p&gt;&lt;p&gt;Related Posts: &lt;a href="/tag/games"&gt;games&lt;/a&gt; &lt;a href="/tag/petersblogger"&gt;petersblogger&lt;/a&gt; &lt;a href="/tag/rails"&gt;rails&lt;/a&gt; &lt;a href="/tag/ruby"&gt;ruby&lt;/a&gt; &lt;a href="/tag/warcraft"&gt;warcraft&lt;/a&gt; &lt;a href="/tag/wow"&gt;wow&lt;/a&gt;&lt;/p&gt;</description>
      <guid>http://www.petersblog.org/node/view/1594</guid>
      <category domain="http://www.technorati.com/tag">games</category>
      <category domain="http://www.technorati.com/tag">petersblogger</category>
      <category domain="http://www.technorati.com/tag">rails</category>
      <category domain="http://www.technorati.com/tag">ruby</category>
      <category domain="http://www.technorati.com/tag">warcraft</category>
      <category domain="http://www.technorati.com/tag">wow</category>
    </item>
    <item>
      <title>Noob guide to the Rails Command Line</title>
      <link>http://www.petersblog.org/node/view/1575</link>
      <description>&lt;p&gt;
One of the things that is offputting about learning any of the web application stacks that I have tried (&lt;a href="/tag/rails"&gt;rails&lt;/a&gt;, &lt;a href="/tag/turbogears"&gt;turbogears&lt;/a&gt;, &lt;a href="/tag/django"&gt;django&lt;/a&gt;) is the command line operations that need to be performed. When you start using them they seem like magic but after a while they become old friends. Hence this article is a summary of the rails command line operations that I use routinely. 
&lt;/p&gt;
&lt;p&gt;
All these commands except for the first one should be executed in the root directory of your application. Note that for the commands that start 'script' you are running ruby scripts and under windows you will need to type 'ruby script/blah'. 
&lt;/p&gt;
&lt;pre class="lazy"&gt;rails {app name}
&lt;/pre&gt;
&lt;p&gt;
The basic one, creates a new rails application. It will create a subdirectory with the given name in the current directory. This subdirectory is the root directory of your application and will be filled with all the appropriate files and subdirectories to start developing. It is a big code generator. 
&lt;/p&gt;
&lt;pre class="lazy"&gt;script/server
&lt;/pre&gt;
&lt;p&gt;
This could be considered the next step after creating your application. You cd to the application's root directory and run this command. It will start a development web server and you will be able to connect to localhost:3000 in a web browser and admire your new creation. This is &lt;i&gt;the&lt;/i&gt; way to debug your application. Any &lt;a href="/node/1570"&gt;print statements&lt;/a&gt; will be dumped out here. 
&lt;/p&gt;
&lt;pre class="lazy"&gt;script/generate model {model name}
&lt;/pre&gt;
&lt;p&gt;
This command will generate a new model in your application. A model is roughly equivalent to a table in the database. 
&lt;/p&gt;
&lt;pre class="lazy"&gt;script/generate controller {controller name}
&lt;/pre&gt;
&lt;p&gt;
This command will generate a new controller in your application in the file 'app/controllers/{controller name}_controller.rb'. A controller is essentially the logic that takes a web page request and decides what to do with it. It will also create a directory called 'app/views/{controller name}' to store the views for this controller, views being the templates that determine how the web pages will look. 
&lt;/p&gt;
&lt;pre class="lazy"&gt;rake db:migrate
&lt;/pre&gt;
&lt;p&gt;
This runs the migrations that will either define the database schema, modify it or undo those modifications. It defaults to updating the development database but can take the argument 'RAILS_ENV=production' to update the production database. 
&lt;/p&gt;
&lt;pre class="lazy"&gt;script/console
&lt;/pre&gt;
&lt;p&gt;
script/console is useful for debugging. It gives you a ruby irb command line prompt where you can play with the inner workings of your code, primarily you have direct access to your models and hence the database. It can be an easier way of manipulating the database from a command line than typing SQL into the mysql or sqlite command line clients. 
&lt;/p&gt;
&lt;pre class="lazy"&gt;rake --tasks
&lt;/pre&gt;
&lt;p&gt;
Lists what rake tasks are available. 
&lt;/p&gt;
&lt;pre class="lazy"&gt;rake db:test:prepare
&lt;/pre&gt;
&lt;p&gt;
Prepares the test database for running unit tests. 
&lt;/p&gt;
&lt;pre class="lazy"&gt;rake test
&lt;/pre&gt;
&lt;p&gt;
Runs all unit, functional and integration tests on your model. 
&lt;/p&gt;
&lt;p&gt;
I think that covers it. There are many more options than these are the ones that I have found to be most useful. 
&lt;/p&gt;&lt;p&gt;Related Posts: &lt;a href="/tag/noob"&gt;noob&lt;/a&gt; &lt;a href="/tag/rails"&gt;rails&lt;/a&gt;&lt;/p&gt;</description>
      <guid>http://www.petersblog.org/node/view/1575</guid>
      <category domain="http://www.technorati.com/tag">noob</category>
      <category domain="http://www.technorati.com/tag">rails</category>
    </item>
    <item>
      <title>Raking in Drupal posts</title>
      <link>http://www.petersblog.org/node/view/1574</link>
      <description>&lt;p&gt;
Rake is the ruby equivalent of make except it is much better in that you are scripting in ruby and not the nasty primeval make language. Rails uses rake a lot as a simple place to gather together a load of administration tasks such as running tests, running database migrations etc. 
&lt;/p&gt;
&lt;p&gt;
When I moved all my drupal blog posts from drupal over to &lt;a href="/tag/petersblogger"&gt;PetersBlogger&lt;/a&gt; I decided to use a nice organised rake script rather than do my usual practise of having a directory full of utility scripts. 
&lt;/p&gt;
&lt;p&gt;
In my rails installation I created a file called lib/tasks/jobs.rake and in it I put my import code: 
&lt;/p&gt;
&lt;pre class="lazy"&gt;&lt;span class="line-numbers"&gt;   1 &lt;/span&gt; task (&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;import_drupal&lt;/span&gt; =&amp;gt; &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;environment&lt;/span&gt;) &lt;span class="Keyword"&gt;do&lt;/span&gt;
&lt;span class="line-numbers"&gt;   2 &lt;/span&gt;   print &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;Importing Drupal&lt;span class="Constant"&gt;\n&lt;/span&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   3 &lt;/span&gt;   oTagTable &lt;span class="Keyword"&gt;=&lt;/span&gt; {}
&lt;span class="line-numbers"&gt;   4 &lt;/span&gt; &lt;span class="Comment"&gt;  &lt;span class="Comment"&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   5 &lt;/span&gt; &lt;span class="Comment"&gt;  &lt;span class="Comment"&gt;#&lt;/span&gt; Import all the articles.&lt;/span&gt;
&lt;span class="line-numbers"&gt;   6 &lt;/span&gt; &lt;span class="Comment"&gt;  &lt;span class="Comment"&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   7 &lt;/span&gt;   &lt;span class="Support"&gt;Drupal&lt;/span&gt;::&lt;span class="Entity"&gt;Article&lt;/span&gt;.&lt;span class="Entity"&gt;find&lt;/span&gt;(&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;all&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;conditions&lt;/span&gt; =&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;type &amp;lt;&amp;gt; 'image'&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;).&lt;span class="Entity"&gt;each&lt;/span&gt; &lt;span class="Keyword"&gt;do &lt;/span&gt;|&lt;span class="Variable"&gt;oArticle&lt;/span&gt;|
&lt;span class="line-numbers"&gt;   8 &lt;/span&gt;     print &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;oArticle&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;title&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="Constant"&gt;\n&lt;/span&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   9 &lt;/span&gt;     oTime &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Support"&gt;Time&lt;/span&gt;.&lt;span class="Entity"&gt;at&lt;/span&gt;( oArticle.&lt;span class="Entity"&gt;created&lt;/span&gt;)
&lt;span class="line-numbers"&gt;  10 &lt;/span&gt;     oPost &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Support"&gt;Post&lt;/span&gt;.&lt;span class="Entity"&gt;create&lt;/span&gt;( &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;nid&lt;/span&gt; =&amp;gt; oArticle.&lt;span class="Entity"&gt;nid&lt;/span&gt;,
&lt;span class="line-numbers"&gt;  11 &lt;/span&gt;                         &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;title&lt;/span&gt; =&amp;gt; oArticle.&lt;span class="Entity"&gt;title&lt;/span&gt;,
&lt;span class="line-numbers"&gt;  12 &lt;/span&gt;       &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;body&lt;/span&gt; =&amp;gt; oArticle.&lt;span class="Entity"&gt;body&lt;/span&gt;,
&lt;span class="line-numbers"&gt;  13 &lt;/span&gt;       &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;created&lt;/span&gt; =&amp;gt; oTime)
&lt;span class="line-numbers"&gt;  14 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  15 &lt;/span&gt; &lt;span class="Comment"&gt;    &lt;span class="Comment"&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  16 &lt;/span&gt; &lt;span class="Comment"&gt;    &lt;span class="Comment"&gt;#&lt;/span&gt; import the tags related to the article.&lt;/span&gt;
&lt;span class="line-numbers"&gt;  17 &lt;/span&gt; &lt;span class="Comment"&gt;    &lt;span class="Comment"&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  18 &lt;/span&gt;     oArticle.&lt;span class="Entity"&gt;tags&lt;/span&gt;.&lt;span class="Entity"&gt;each&lt;/span&gt; &lt;span class="Keyword"&gt;do &lt;/span&gt;|&lt;span class="Variable"&gt;oTag&lt;/span&gt;|
&lt;span class="line-numbers"&gt;  19 &lt;/span&gt;       &lt;span class="Keyword"&gt;if&lt;/span&gt; oTagTable[oTag.&lt;span class="Entity"&gt;tag&lt;/span&gt;]
&lt;span class="line-numbers"&gt;  20 &lt;/span&gt;         oNewTag &lt;span class="Keyword"&gt;=&lt;/span&gt; oTagTable[oTag.&lt;span class="Entity"&gt;tag&lt;/span&gt;]
&lt;span class="line-numbers"&gt;  21 &lt;/span&gt;       &lt;span class="Keyword"&gt;else&lt;/span&gt;
&lt;span class="line-numbers"&gt;  22 &lt;/span&gt;         oNewTag &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Support"&gt;Tag&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt;( &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;tag&lt;/span&gt;=&amp;gt;oTag.&lt;span class="Entity"&gt;tag&lt;/span&gt;)
&lt;span class="line-numbers"&gt;  23 &lt;/span&gt;         oTagTable[oTag.&lt;span class="Entity"&gt;tag&lt;/span&gt;] &lt;span class="Keyword"&gt;=&lt;/span&gt; oNewTag
&lt;span class="line-numbers"&gt;  24 &lt;/span&gt;       &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  25 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  26 &lt;/span&gt;       oPost.&lt;span class="Entity"&gt;tags&lt;/span&gt; &lt;span class="Keyword"&gt;&amp;lt;&amp;lt;&lt;/span&gt; oNewTag
&lt;span class="line-numbers"&gt;  27 &lt;/span&gt;     &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  28 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  29 &lt;/span&gt; &lt;span class="Comment"&gt;    &lt;span class="Comment"&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  30 &lt;/span&gt; &lt;span class="Comment"&gt;    &lt;span class="Comment"&gt;#&lt;/span&gt; Import the comments for the article.&lt;/span&gt;
&lt;span class="line-numbers"&gt;  31 &lt;/span&gt; &lt;span class="Comment"&gt;    &lt;span class="Comment"&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  32 &lt;/span&gt;     oArticle.&lt;span class="Entity"&gt;comments&lt;/span&gt;.&lt;span class="Entity"&gt;each&lt;/span&gt; &lt;span class="Keyword"&gt;do &lt;/span&gt;|&lt;span class="Variable"&gt;oComment&lt;/span&gt;|
&lt;span class="line-numbers"&gt;  33 &lt;/span&gt;       strWho &lt;span class="Keyword"&gt;=&lt;/span&gt; oComment.&lt;span class="Entity"&gt;name&lt;/span&gt;
&lt;span class="line-numbers"&gt;  34 &lt;/span&gt;       strComment &lt;span class="Keyword"&gt;=&lt;/span&gt; oComment.&lt;span class="Entity"&gt;comment&lt;/span&gt;
&lt;span class="line-numbers"&gt;  35 &lt;/span&gt;       &lt;span class="Keyword"&gt;if&lt;/span&gt; strWho &lt;span class="Keyword"&gt;==&lt;/span&gt; &lt;span class="Constant"&gt;nil&lt;/span&gt; &lt;span class="Keyword"&gt;or&lt;/span&gt; strWho &lt;span class="Keyword"&gt;==&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  36 &lt;/span&gt;         strWho &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;Peter&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  37 &lt;/span&gt;       &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  38 &lt;/span&gt;       &lt;span class="Keyword"&gt;if&lt;/span&gt; strComment &lt;span class="Keyword"&gt;==&lt;/span&gt; &lt;span class="Constant"&gt;nil&lt;/span&gt; &lt;span class="Keyword"&gt;or&lt;/span&gt; strComment.&lt;span class="Entity"&gt;strip&lt;/span&gt; &lt;span class="Keyword"&gt;==&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  39 &lt;/span&gt;         strComment &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;{nothing to say}&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  40 &lt;/span&gt;       &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  41 &lt;/span&gt;       oPost.&lt;span class="Entity"&gt;comments&lt;/span&gt; &lt;span class="Keyword"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="Support"&gt;Comment&lt;/span&gt;.&lt;span class="Entity"&gt;create&lt;/span&gt;( &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;who&lt;/span&gt; =&amp;gt; strWho,
&lt;span class="line-numbers"&gt;  42 &lt;/span&gt;           &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;body&lt;/span&gt; =&amp;gt; strComment,
&lt;span class="line-numbers"&gt;  43 &lt;/span&gt;           &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;created&lt;/span&gt; =&amp;gt; &lt;span class="Support"&gt;Time&lt;/span&gt;.&lt;span class="Entity"&gt;at&lt;/span&gt;( oComment.&lt;span class="Entity"&gt;timestamp&lt;/span&gt;), &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;published&lt;/span&gt; =&amp;gt; &lt;span class="Constant"&gt;1&lt;/span&gt;)
&lt;span class="line-numbers"&gt;  44 &lt;/span&gt;       &lt;span class="Keyword"&gt;if&lt;/span&gt; &lt;span class="Keyword"&gt;not&lt;/span&gt; oPost.&lt;span class="Entity"&gt;comments&lt;/span&gt;[&lt;span class="Keyword"&gt;-&lt;/span&gt;&lt;span class="Constant"&gt;1&lt;/span&gt;].&lt;span class="Entity"&gt;valid?&lt;/span&gt;
&lt;span class="line-numbers"&gt;  45 &lt;/span&gt;         p oPost.&lt;span class="Entity"&gt;comments&lt;/span&gt;[&lt;span class="Keyword"&gt;-&lt;/span&gt;&lt;span class="Constant"&gt;1&lt;/span&gt;]
&lt;span class="line-numbers"&gt;  46 &lt;/span&gt;         p oPost.&lt;span class="Entity"&gt;comments&lt;/span&gt;[&lt;span class="Keyword"&gt;-&lt;/span&gt;&lt;span class="Constant"&gt;1&lt;/span&gt;].&lt;span class="Entity"&gt;errors&lt;/span&gt;
&lt;span class="line-numbers"&gt;  47 &lt;/span&gt;       &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  48 &lt;/span&gt;     &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  49 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  50 &lt;/span&gt;     oPost.&lt;span class="Entity"&gt;save!&lt;/span&gt;
&lt;span class="line-numbers"&gt;  51 &lt;/span&gt;   &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  52 &lt;/span&gt; &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;
This creates a rake task called 'import_drupal' which I can invoke simply by running 
&lt;/p&gt;
&lt;pre class="lazy"&gt;rake import_drupal
&lt;/pre&gt;
&lt;p&gt;
The task runs with the appropriate rails environment so all my models are in place. I had to create a model to access the records in the drupal database thus: 
&lt;/p&gt;
&lt;pre class="lazy"&gt;&lt;span class="line-numbers"&gt;   1 &lt;/span&gt; &lt;span class="Keyword"&gt;module&lt;/span&gt; &lt;span class="Entity"&gt;Drupal&lt;/span&gt;
&lt;span class="line-numbers"&gt;   2 &lt;/span&gt;   &lt;span class="Keyword"&gt;class&lt;/span&gt; &lt;span class="Entity"&gt;Comment&lt;span class="Superclass"&gt; &lt;span class="Superclass"&gt;&amp;lt;&lt;/span&gt; ActiveRecord::Base&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   3 &lt;/span&gt;     establish_connection configurations[&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;drupal&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;]
&lt;span class="line-numbers"&gt;   4 &lt;/span&gt;     set_table_name &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;comments&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   5 &lt;/span&gt;   &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;   6 &lt;/span&gt;   &lt;span class="Keyword"&gt;class&lt;/span&gt; &lt;span class="Entity"&gt;Tag&lt;span class="Superclass"&gt; &lt;span class="Superclass"&gt;&amp;lt;&lt;/span&gt; ActiveRecord::Base&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   7 &lt;/span&gt;     establish_connection configurations[&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;drupal&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;]
&lt;span class="line-numbers"&gt;   8 &lt;/span&gt;     set_primary_key &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;tid&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   9 &lt;/span&gt;     has_and_belongs_to_many &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;articles&lt;/span&gt;,
&lt;span class="line-numbers"&gt;  10 &lt;/span&gt;       &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;class_name&lt;/span&gt; =&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;Drupal::Article&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;,
&lt;span class="line-numbers"&gt;  11 &lt;/span&gt;       &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;join_table&lt;/span&gt; =&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;awtags_node&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;,
&lt;span class="line-numbers"&gt;  12 &lt;/span&gt;       &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;foreign_key&lt;/span&gt; =&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;tid&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;,
&lt;span class="line-numbers"&gt;  13 &lt;/span&gt;       &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;association_foreign_key&lt;/span&gt; =&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;nid&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  14 &lt;/span&gt;     set_table_name &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;awtags&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  15 &lt;/span&gt;   &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  16 &lt;/span&gt;   &lt;span class="Keyword"&gt;class&lt;/span&gt; &lt;span class="Entity"&gt;Article&lt;span class="Superclass"&gt; &lt;span class="Superclass"&gt;&amp;lt;&lt;/span&gt; ActiveRecord::Base&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  17 &lt;/span&gt;     establish_connection configurations[&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;drupal&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;]
&lt;span class="line-numbers"&gt;  18 &lt;/span&gt;     set_primary_key &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;nid&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  19 &lt;/span&gt;     &lt;span class="Variable"&gt;self&lt;/span&gt;.&lt;span class="Entity"&gt;inheritance_column&lt;/span&gt; &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;poopy&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  20 &lt;/span&gt;     has_many &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;comments&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;table_name&lt;/span&gt; =&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;comments&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;foreign_key&lt;/span&gt; =&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;nid&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;class_name&lt;/span&gt; =&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;Drupal::Comment&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  21 &lt;/span&gt;     has_and_belongs_to_many &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;tags&lt;/span&gt;,
&lt;span class="line-numbers"&gt;  22 &lt;/span&gt;       &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;class_name&lt;/span&gt; =&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;Drupal::Tag&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;,
&lt;span class="line-numbers"&gt;  23 &lt;/span&gt;       &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;join_table&lt;/span&gt; =&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;awtags_node&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;,
&lt;span class="line-numbers"&gt;  24 &lt;/span&gt;       &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;foreign_key&lt;/span&gt; =&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;nid&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;,
&lt;span class="line-numbers"&gt;  25 &lt;/span&gt;       &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;association_foreign_key&lt;/span&gt; =&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;tid&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  26 &lt;/span&gt;     set_table_name &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;node&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  27 &lt;/span&gt;   &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  28 &lt;/span&gt; &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;
This model handles all the relationships between the records, including the has_and_belongs_to_many relationship between posts and tags (provided by &lt;a href="/tag/awtags"&gt;awtags&lt;/a&gt;). 
&lt;/p&gt;
&lt;p&gt;
I needed an entry in config/database.yml to define the connection to the drupal database: 
&lt;/p&gt;
&lt;pre class="lazy"&gt;drupal:
  adapter: mysql
  database: petersblog
  username: secret
  password: doyouthinkidpostithere
&lt;/pre&gt;
&lt;p&gt;
and it all works nicely. If I invoke it thusly: 
&lt;/p&gt;
&lt;pre class="lazy"&gt;rake import_drupal RAILS_ENV=production
&lt;/pre&gt;
&lt;p&gt;
then it will import the posts into the rails production database. 
&lt;/p&gt;
&lt;p&gt;
I'm giving away a few of my schema secrets here which pedantic rails developers might leap on, specifically I have used my own fields called 'nid' and 'created' to perform roles that rails already provides 'id' and 'created_on' for. The reason for the duplication was that during development I was having problems setting these fields to the values from the drupal database, rails was insisting on giving them it's own values. By having my own fields I had total control. There are no doubt ways around this but I didn't have time to find them (although I did find other people moaning about the same problem). 
&lt;/p&gt;
&lt;p&gt;
And that's how I managed to save 1500 posts from the hell that is php. 
&lt;/p&gt;&lt;p&gt;Related Posts: &lt;a href="/tag/petersblogger"&gt;petersblogger&lt;/a&gt; &lt;a href="/tag/rails"&gt;rails&lt;/a&gt; &lt;a href="/tag/rake"&gt;rake&lt;/a&gt;&lt;/p&gt;</description>
      <guid>http://www.petersblog.org/node/view/1574</guid>
      <category domain="http://www.technorati.com/tag">petersblogger</category>
      <category domain="http://www.technorati.com/tag">rails</category>
      <category domain="http://www.technorati.com/tag">rake</category>
    </item>
  </channel>
</rss>
