<?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 ruby</title>
    <link>http://www.petersblog.org/</link>
    <description>Nodes containing the tag ruby</description>
    <item>
      <title>Importing RSS into Blog</title>
      <link>http://www.petersblog.org/node/view/1734</link>
      <description>&lt;p&gt;
This is how I gather my twitterings into the blog. I just scrape the rss feed. The twitter api may allow for something more sophisticated but this Just Works. 
&lt;/p&gt;
&lt;p&gt;
It's just too easy in ruby/rails: 
&lt;/p&gt;
&lt;pre class="lazy"&gt;&lt;span class="line-numbers"&gt;   1 &lt;/span&gt; &lt;span class="Keyword"&gt;class&lt;/span&gt; &lt;span class="Entity"&gt;TwitterHandler&lt;/span&gt;
&lt;span class="line-numbers"&gt;   2 &lt;/span&gt;   &lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;update&lt;/span&gt;(&lt;span class="Variable"&gt; strFile&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;open-uri&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   4 &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;simple-rss&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   5 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;   6 &lt;/span&gt;     oRss &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Support"&gt;SimpleRSS&lt;/span&gt;.&lt;span class="Entity"&gt;parse&lt;/span&gt;( &lt;span class="Entity"&gt;open&lt;/span&gt;( strFile))
&lt;span class="line-numbers"&gt;   7 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;   8 &lt;/span&gt;     oRss.&lt;span class="Entity"&gt;items&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;oItem&lt;/span&gt;|
&lt;span class="line-numbers"&gt;   9 &lt;/span&gt;       strBody &lt;span class="Keyword"&gt;=&lt;/span&gt; oItem.&lt;span class="Entity"&gt;description&lt;/span&gt;
&lt;span class="line-numbers"&gt;  10 &lt;/span&gt;       strBody.&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;^petersblog: &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&gt;&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;)
&lt;span class="line-numbers"&gt;  11 &lt;/span&gt;       strTitle &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;Twittering: &lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;oItem&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;pubDate&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;strftime&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;(&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;%d %B %Y %H:%M&lt;span class="String"&gt;&amp;quot;&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;  12 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  13 &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;Post&lt;/span&gt;.&lt;span class="Entity"&gt;find_by_title&lt;/span&gt;( strTitle)
&lt;span class="line-numbers"&gt;  14 &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;new&lt;/span&gt;( &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;title&lt;/span&gt; =&amp;gt; strTitle, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;body&lt;/span&gt; =&amp;gt; strBody, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;created&lt;/span&gt; =&amp;gt; oItem.&lt;span class="Entity"&gt;pubDate&lt;/span&gt;)
&lt;span class="line-numbers"&gt;  15 &lt;/span&gt;         oPost.&lt;span class="Entity"&gt;Taglist&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;twittering&lt;span class="String"&gt;&amp;quot;&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="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  18 &lt;/span&gt;   &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  19 &lt;/span&gt; &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  20 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  21 &lt;/span&gt; &lt;span class="Variable"&gt;The&lt;/span&gt; above is only wrapped &lt;span class="Keyword"&gt;in&lt;/span&gt; a &lt;span class="Keyword"&gt;class&lt;/span&gt; because that is how the first rspec example &lt;span class="Variable"&gt;I&lt;/span&gt; read was laid out.
&lt;span class="line-numbers"&gt;  22 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  23 &lt;/span&gt; &lt;span class="Variable"&gt;Then&lt;/span&gt; there is a rake task to run the above:
&lt;span class="line-numbers"&gt;  24 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  25 &lt;/span&gt; &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;ruby&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  26 &lt;/span&gt; task (&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;twitter&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;  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;lib/TwitterHandler&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;   oTwitter &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Support"&gt;TwitterHandler&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt;
&lt;span class="line-numbers"&gt;  30 &lt;/span&gt;   oTwitter.&lt;span class="Entity"&gt;update&lt;/span&gt;( &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;http://twitter.com/statuses/user_timeline/16738335.rss&lt;span class="String"&gt;&amp;quot;&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;/pre&gt;
&lt;p&gt;
Then a cron job: 
&lt;/p&gt;
&lt;pre class="lazy"&gt;13,28,43,58 * * * *  bash -c &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;cd /var/www/PetersBlogger/current; rake -s RAILS_ENV=production twitter&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;
This polls every 15 minutes. The '-s' stops rake outputting a needless 'In directory blah' message which cron dutifully emails to me. 
&lt;/p&gt;
&lt;p&gt;
I'm enjoying using twitter, I like the medium, short and pithy. Not really suitable for code snippets though. 
&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;a href="/tag/twitter"&gt;twitter&lt;/a&gt;&lt;/p&gt;</description>
      <guid>http://www.petersblog.org/node/view/1734</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>
      <category domain="http://www.technorati.com/tag">twitter</category>
    </item>
    <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>Netbeans and Cygwin Ruby</title>
      <link>http://www.petersblog.org/node/view/1631</link>
      <description>&lt;p&gt;
&lt;a href="/tag/netbeans"&gt;Netbeans&lt;/a&gt; and &lt;a href="/tag/cygwin"&gt;cygwin&lt;/a&gt; &lt;a href="/tag/ruby"&gt;ruby&lt;/a&gt; don't have a happy relationship. To use the cygwin ruby interpreter it is necessary to launch Netbeans (6.1) with the arguments: 
&lt;/p&gt;
&lt;div class="verbatim-block"&gt;&lt;pre&gt;-J-Druby.no.sync-stdio=true
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;
as otherwise Netbeans will pass a windows-style file name to the cygwin ruby that the latter cannot recognise (C:\Projects\Blah instead of /cygdrive/c/Projects/Blah). Unfortunately the ruby interpreter configuration in Netbeans tries to be clever and insists you point it to a ruby executable, you can't give it a batch file that will hack things into working shape. 
&lt;/p&gt;
&lt;p&gt;
rspec on cygwin has some similar problems. Here are some hacks to the rspec plugin to make it work from netbeans: 
&lt;/p&gt;
&lt;p&gt;
In vendor/plugins/rspec/bin/bin, change: 
&lt;/p&gt;
&lt;pre class="lazy"&gt;&lt;span class="Variable"&gt;&lt;span class="Variable"&gt;$&lt;/span&gt;LOAD_PATH&lt;/span&gt;.&lt;span class="Entity"&gt;unshift&lt;/span&gt;(&lt;span class="Support"&gt;File&lt;/span&gt;.&lt;span class="Entity"&gt;expand_path&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;/../lib&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;))
&lt;/pre&gt;
&lt;p&gt;
to 
&lt;/p&gt;
&lt;pre class="lazy"&gt;&lt;span class="Variable"&gt;&lt;span class="Variable"&gt;$&lt;/span&gt;LOAD_PATH&lt;/span&gt;.&lt;span class="Entity"&gt;unshift&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="Entity"&gt;sub&lt;/span&gt;( &lt;span class="String"&gt;&lt;span class="String"&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;C:&lt;span class="Constant"&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="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;/cygdrive/c/&lt;span class="String"&gt;'&lt;/span&gt;&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;/../lib&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)
&lt;/pre&gt;
&lt;p&gt;
In vendor/plugins/rspec/lib/spec/runner/example_group_runner.rb, change: 
&lt;/p&gt;
&lt;pre class="lazy"&gt;load file
&lt;/pre&gt;
&lt;p&gt;
to 
&lt;/p&gt;
&lt;pre class="lazy"&gt;load &lt;span class="Support"&gt;File&lt;/span&gt;.&lt;span class="Entity"&gt;expand_path&lt;/span&gt;(file).&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;C:&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&gt;/cygdrive/c&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;)
&lt;/pre&gt;
&lt;p&gt;
Tip: to diagnose problems with 'require' not finding files, put this: 
&lt;/p&gt;
&lt;pre class="lazy"&gt;p &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;$&lt;/span&gt;:&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;
before the failing line to see what directories are in the load path. If you see 'C:\' and it's cygwin then fix it. 
&lt;/p&gt;
&lt;p&gt;
Unfortunately it takes a good 20 seconds for cygwin/rspec to run a trivial test script. The rspec server starts and runs but netbeans/rspec seem reluctant to use it (it runs tests ok from the command line). 
&lt;/p&gt;
&lt;p&gt;
Why am I still bothering with cygwin? Because I still want to test gcc C code by compiling it into ruby extensions and using ruby test codes (rspec or unit testing). 
&lt;/p&gt;
&lt;p&gt;
In tracing these problems I came across more reasons that Netbeans is worth the hassle: 
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;
ctrl-tab to switch to last open tab. ctrl and hold tab brings up a handy little context menu to choose a tab from (kinda like windows task switching with tab). 
&lt;/li&gt;
&lt;li&gt;
press ctrl and mouse over an identifier to bring up rdoc info about it. Ctrl-click to go to it's definition. 
&lt;/li&gt;
&lt;li&gt;
alt-b to go back to where you came from. 
&lt;/li&gt;
&lt;li&gt;
click on an 'end' and the matching begin/do/if is highlighted and vice versa, which is damn useful. 
&lt;/li&gt;
&lt;li&gt;
select an identifier and all matching identifiers in your file are automatically highlighted. 
&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Related Posts: &lt;a href="/tag/cygwin"&gt;cygwin&lt;/a&gt; &lt;a href="/tag/netbeans"&gt;netbeans&lt;/a&gt; &lt;a href="/tag/rspec"&gt;rspec&lt;/a&gt; &lt;a href="/tag/ruby"&gt;ruby&lt;/a&gt;&lt;/p&gt;</description>
      <guid>http://www.petersblog.org/node/view/1631</guid>
      <category domain="http://www.technorati.com/tag">cygwin</category>
      <category domain="http://www.technorati.com/tag">netbeans</category>
      <category domain="http://www.technorati.com/tag">rspec</category>
      <category domain="http://www.technorati.com/tag">ruby</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>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>Testing Firmware Code</title>
      <link>http://www.petersblog.org/node/view/1581</link>
      <description>&lt;p&gt;
&lt;a href="/node/1542"&gt;I've described before&lt;/a&gt; how I've been testing Firmware code written in C by compiling it into Ruby Extensions and using ruby's excellent unit testing framework. 
&lt;/p&gt;
&lt;p&gt;
Up till now I've been testing the 'middle tier' algorithmic code but how to go about testing the low level stuff, that which pokes the hardware? 
&lt;/p&gt;
&lt;p&gt;
The firmware I am developing is for the ST ARM750 microcontroller and I am using ST's own standard library which is very useful driver code for all the peripherals embedded on the chip. It is a very useful resource as the peripherals on this chip are very complex and the user manual is written in hardware engineer gobbledygook that you have to study like a legal document, looking for the small print (e.g. running it at 60MHz it kept crashing until I found the small print that advised me to enable 'burst mode'). 
&lt;/p&gt;
&lt;p&gt;
Scanning through the source to this library I saw some #ifdef DEBUG statements that implied that it could be compiled into an emulation library. The code was written very cleanly so it took me very little time to write something in (of course) ruby to scan the header files and generate 5250 lines of C code to emulate this library. Here is an example of one of the generated library functions: 
&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;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   2 &lt;/span&gt; &lt;span class="Comment"&gt; * Emulate the function TIM_ClearFlag&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="Keyword"&gt;void&lt;/span&gt; TIM_ClearFlag(TIM_TypeDef* TIMx, u16 TIM_FLAG)
&lt;span class="line-numbers"&gt;   5 &lt;/span&gt; {
&lt;span class="line-numbers"&gt;   6 &lt;/span&gt;   VALUE nRet;
&lt;span class="line-numbers"&gt;   7 &lt;/span&gt;   VALUE oArgs = rb_ary_new();
&lt;span class="line-numbers"&gt;   8 &lt;/span&gt;   VALUE strFuncName = rb_str_new2( &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;TIM_ClearFlag&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;);
&lt;span class="line-numbers"&gt;   9 &lt;/span&gt;   ID nId = rb_intern( &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;Function&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;);
&lt;span class="line-numbers"&gt;  10 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  11 &lt;/span&gt;   rb_ary_push( oArgs, PassStructureArg( (&lt;span class="Keyword"&gt;void&lt;/span&gt; *)TIMx, &lt;span class="Keyword"&gt;sizeof&lt;/span&gt;( TIM_TypeDef)));
&lt;span class="line-numbers"&gt;  12 &lt;/span&gt;   rb_ary_push( oArgs, LONG2NUM( TIM_FLAG));
&lt;span class="line-numbers"&gt;  13 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  14 &lt;/span&gt;   nRet = rb_funcall( g_oCallBack, nId, &lt;span class="Constant"&gt;2&lt;/span&gt;, strFuncName, oArgs);
&lt;span class="line-numbers"&gt;  15 &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; nRet is not used &lt;span class="Comment"&gt;*/&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  17 &lt;/span&gt; }
&lt;/pre&gt;
&lt;p&gt;
TIM_ClearFlag is the name of the function in the ST standard library. The emulation code takes the name of the emulated function and the two arguments to the function, converts them into ruby objects and passes them to a method called 'Function' in a ruby object. In the testing case, this object is something that will make sure the functions are being called in the correct sequence and with the correct parameters and will return the appropriate return value. Structure arguments are passed as binary objects which the ruby can decipher from the structure definitions in the ST library header files (also parsed using ruby). 
&lt;/p&gt;
&lt;p&gt;
Note that my system has a few limitations that are acceptable because of the simplicity of the ST library and the way it was coded: 
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;
The parser I wrote just about handles the coding style of this library 
&lt;/li&gt;
&lt;li&gt;
The calls to ruby do not support the case where the library may poke the values within a structure passed to it by pointer. 
&lt;/li&gt;
&lt;li&gt;
It doesn't handle passing structures by value. 
&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;
Now I have my low level testing library I will be able to do my testing using something I call signature analysis (I'm not sure there is a better software engineering term). I write the code, test it on the real hardware, then I can record the sequence of function calls made including the values of function arguments. Later on, in leiu of real hardware to test it on I can run the test code and compare it against the previously recorded signatures. Et voila, the reassurance that I haven't broken anything and I can sleep at night. 
&lt;/p&gt;
&lt;p&gt;
UPDATE: 
&lt;/p&gt;
&lt;p&gt;
My test library now includes a YamlFaker that can be used in two modes: 
&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;
it will record a sequence of function calls 
&lt;/li&gt;
&lt;li&gt;
it will ensure a sequence of function calls matches a previous recording 
&lt;/li&gt;&lt;/ol&gt;

&lt;p&gt;
The yaml script looks like this: 
&lt;/p&gt;
&lt;div class="verbatim-block"&gt;&lt;pre&gt;#
# This file is maintained by the YamlFaker module DO NOT EDIT
#
---
- test_CommandPWMSet Demand -200:
  - :Comment: |-

      Looking for demand to be correct according to the requested range and the
      scaling factors. Also ensure that for the fifth channel, the period is
      large for small demand as the output of this channel is inverted
  - TIM_SetPulse:
      args:
      - TIMx: TIM0
      - TIM_Channel: &amp;quot;0x2&amp;quot;
      - Pulse: &amp;quot;0x0&amp;quot;
  - TIM_SetPulse:
      args:
      - TIMx: TIM1
      - TIM_Channel: &amp;quot;0x2&amp;quot;
      - Pulse: &amp;quot;0x0&amp;quot;
  - TIM_SetPulse:
      args:
      - TIMx: TIM2
      - TIM_Channel: &amp;quot;0x2&amp;quot;
      - Pulse: &amp;quot;0x0&amp;quot;
  - PWM_SetPulse:
      args:
      - PWM_Channel: &amp;quot;0x2&amp;quot;
      - Pulse: &amp;quot;0x0&amp;quot;
  - PWM_SetPulse:
      args:
      - PWM_Channel: &amp;quot;0x4&amp;quot;
      - Pulse: &amp;quot;0x12c&amp;quot;
- test_CommandPWMSet Demand -100:
  - TIM_SetPulse:
      args:
      - TIMx: TIM0
      - TIM_Channel: &amp;quot;0x2&amp;quot;
      - Pulse: &amp;quot;0x0&amp;quot;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;
Most of the entries in the script describe the expected sequence of function calls, complete with arguments. I can ensure that the demand being sent to my PWM channels is what I expect. These recorded scripts could be quite useful, it is a complete log of the interaction with the hardware. If I ever had to change the code that poked the hardware I can compare the script recorded from the new code against my old script and make sure that any changes are what I would expect. This way I can be far more certain that my changes are correct before they hit real hardware. 
&lt;/p&gt;&lt;p&gt;Related Posts: &lt;a href="/tag/c"&gt;c&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/1581</guid>
      <category domain="http://www.technorati.com/tag">c</category>
      <category domain="http://www.technorati.com/tag">ruby</category>
      <category domain="http://www.technorati.com/tag">testing</category>
    </item>
    <item>
      <title>Problems with Multiple Ruby Extensions</title>
      <link>http://www.petersblog.org/node/view/1572</link>
      <description>&lt;p&gt;
I've been testing my C libraries by compiling them into ruby extensions and then using ruby's unit testing to test them. Under Windows I had it all running nicely and I could run a main test script that would invoke all the other test scripts. Sweet. 
&lt;/p&gt;
&lt;p&gt;
Then it came to getting it running under linux (the target system) and while the unit tests ran fine individually, when I tried my main test script I was getting weird errors from my test extensions, including segment violations (!). 
&lt;/p&gt;
&lt;p&gt;
It took me a while to track down the problem but it turned out to be due to the behaviour of gcc and the linux program loader. All my test libraries were loaded into one ruby process and because they all contained functions with similar names it became a bit of a lottery as to which function in which library was called. I am used to the behaviour of windows dll's where all functions are by default hidden from the outside world unless you explicity export them. gcc/elf/whatever seems to export everything from shared libraries (linux equivalent of a dll) by default with hilarious results. 
&lt;/p&gt;
&lt;p&gt;
In version 4 of gcc they have added a facility for hiding these internal functions. I solved my problem as follows: 
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;
Add '-fvisibility=hidden' to the gcc arguments to cause all functions to be hidden by default. 
&lt;/li&gt;
&lt;li&gt;
Add this code to my stddefs.h: 
&lt;pre class="lazy"&gt;&lt;span class="line-numbers"&gt;   1 &lt;/span&gt; #&lt;span class="Keyword"&gt;ifdef&lt;/span&gt; _MSC_VER
&lt;span class="line-numbers"&gt;   2 &lt;/span&gt;   #&lt;span class="Keyword"&gt;ifdef&lt;/span&gt; BUILDING_DLL
&lt;span class="line-numbers"&gt;   3 &lt;/span&gt;     #&lt;span class="Keyword"&gt;define&lt;/span&gt; &lt;span class="Entity"&gt;DLLEXPORT&lt;/span&gt; &lt;span class="Entity"&gt;__declsp&lt;span class="Entity"&gt;ec&lt;/span&gt;&lt;/span&gt;(dllexport)
&lt;span class="line-numbers"&gt;   4 &lt;/span&gt;   #&lt;span class="Keyword"&gt;else&lt;/span&gt;
&lt;span class="line-numbers"&gt;   5 &lt;/span&gt;     #&lt;span class="Keyword"&gt;define&lt;/span&gt; &lt;span class="Entity"&gt;DLLEXPORT&lt;/span&gt; &lt;span class="Entity"&gt;__declsp&lt;span class="Entity"&gt;ec&lt;/span&gt;&lt;/span&gt;(dllimport)
&lt;span class="line-numbers"&gt;   6 &lt;/span&gt;   #&lt;span class="Keyword"&gt;endif&lt;/span&gt;
&lt;span class="line-numbers"&gt;   7 &lt;/span&gt;   #&lt;span class="Keyword"&gt;define&lt;/span&gt; &lt;span class="Entity"&gt;DLLLOCAL&lt;/span&gt;
&lt;span class="line-numbers"&gt;   8 &lt;/span&gt; #&lt;span class="Keyword"&gt;else&lt;/span&gt;
&lt;span class="line-numbers"&gt;   9 &lt;/span&gt;   #&lt;span class="Keyword"&gt;define&lt;/span&gt; &lt;span class="Entity"&gt;DLLEXPORT&lt;/span&gt; __attribute__ ((visibility(&lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;default&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)))
&lt;span class="line-numbers"&gt;  10 &lt;/span&gt;   #&lt;span class="Keyword"&gt;define&lt;/span&gt; &lt;span class="Entity"&gt;DLLLOCAL&lt;/span&gt; __attribute__ ((visibility(&lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;hidden&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)))
&lt;span class="line-numbers"&gt;  11 &lt;/span&gt; #&lt;span class="Keyword"&gt;endif&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
Change the functions that ruby calls to load the ruby extension to the following form (i.e. add DLLEXPORT): 
&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;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   2 &lt;/span&gt; &lt;span class="Comment"&gt; * Initialise the test class.&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; DLLEXPORT &lt;span class="Keyword"&gt;void&lt;/span&gt; &lt;span class="Entity"&gt;Init_testtim&lt;span class="Entity"&gt;er&lt;/span&gt;&lt;/span&gt;( &lt;span class="Keyword"&gt;void&lt;/span&gt;)
&lt;span class="line-numbers"&gt;   5 &lt;/span&gt; {
&lt;span class="line-numbers"&gt;   6 &lt;/span&gt;     VALUE oMyClass = rb_define_class( &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;TestTimer&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, rb_cObject);
&lt;span class="line-numbers"&gt;   7 &lt;/span&gt;     rb_define_method( oMyClass, &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;initialize&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, test_initialise, &lt;span class="Constant"&gt;1&lt;/span&gt;);
&lt;span class="line-numbers"&gt;   8 &lt;/span&gt;     rb_define_method( oMyClass, &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;TimerSet&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, test_TimerSet, &lt;span class="Constant"&gt;1&lt;/span&gt;);
&lt;span class="line-numbers"&gt;   9 &lt;/span&gt;     rb_define_method( oMyClass, &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;TimerUpdate&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, test_TimerUpdate, &lt;span class="Constant"&gt;1&lt;/span&gt;);
&lt;span class="line-numbers"&gt;  10 &lt;/span&gt;     rb_define_method( oMyClass, &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;TimerStart&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, test_TimerStart, &lt;span class="Constant"&gt;1&lt;/span&gt;);
&lt;span class="line-numbers"&gt;  11 &lt;/span&gt;     rb_define_method( oMyClass, &lt;span class="String"&gt;&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;TimerTest&lt;span class="String"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, test_TimerTest, &lt;span class="Constant"&gt;0&lt;/span&gt;);
&lt;span class="line-numbers"&gt;  12 &lt;/span&gt; }
&lt;/pre&gt;
&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;
These functions are the only ones that need to be visible outside the library. 
&lt;/p&gt;
&lt;p&gt;
Results: 
&lt;/p&gt;
&lt;div style="text-align:center"&gt;&lt;img src="/images/TestsDone.gif" alt="images/TestsDone.gif"/&gt;&lt;/div&gt; &lt;p&gt;Related Posts: &lt;a href="/tag/c"&gt;c&lt;/a&gt; &lt;a href="/tag/gcc"&gt;gcc&lt;/a&gt; &lt;a href="/tag/linux"&gt;linux&lt;/a&gt; &lt;a href="/tag/ruby"&gt;ruby&lt;/a&gt;&lt;/p&gt;</description>
      <guid>http://www.petersblog.org/node/view/1572</guid>
      <category domain="http://www.technorati.com/tag">c</category>
      <category domain="http://www.technorati.com/tag">gcc</category>
      <category domain="http://www.technorati.com/tag">linux</category>
      <category domain="http://www.technorati.com/tag">ruby</category>
    </item>
  </channel>
</rss>
