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

<channel>
	<title>markbennett.ca</title>
	<atom:link href="http://markbennett.ca/feed/" rel="self" type="application/rss+xml" />
	<link>http://markbennett.ca</link>
	<description>Code, travel, and adventure!</description>
	<lastBuildDate>Wed, 03 Mar 2010 17:40:07 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Notes from Edmonton&#8217;s First Ruby Meetup</title>
		<link>http://markbennett.ca/2010/03/edmontons-first-ruby-meetup/</link>
		<comments>http://markbennett.ca/2010/03/edmontons-first-ruby-meetup/#comments</comments>
		<pubDate>Wed, 03 Mar 2010 17:40:07 +0000</pubDate>
		<dc:creator>Mark</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[edmonton]]></category>
		<category><![CDATA[meetup]]></category>
		<category><![CDATA[notes]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[yeg]]></category>
		<category><![CDATA[yegrb]]></category>

		<guid isPermaLink="false">http://markbennett.ca/?p=338</guid>
		<description><![CDATA[Last night was the first gathering of the Edmonton Ruby Meetup.  The purpose of these meetups is to build and spotlight the Ruby community in the Edmonton area, and to share our experiences with Ruby, Rails and software development in general.  Anyone interested is welcome to attend and speak.  If you&#8217;re not already you should [...]]]></description>
			<content:encoded><![CDATA[<p>Last night was the first gathering of the Edmonton Ruby Meetup.  The purpose of these meetups is to build and spotlight the Ruby community in the Edmonton area, and to share our experiences with Ruby, Rails and software development in general.  Anyone interested is welcome to attend and speak.  If you&#8217;re not already you should start following us on Twitter at <a href="http://twitter.com/yegrb">@yegrb</a> for announcements and news.</p>
<p><a href="http://www.nexopia.com/about">Nexopia</a> kindly donate space and Internet, and the turnout was good.  We ended up all fitting into one board room so it was a good opportunity to meet other Ruby enthusiasts face-to-face.</p>
<p><span id="more-338"></span></p>
<h2>Introductions</h2>
<p>The meetup started by going round the table to do some introductions.  Since this was our first meetup there were lots of new people to introduce.  All of the attendees with Twitter accounts were added to our <a href="http://twitter.com/yegrb/edmonton-rubyists">Edmonton Rubyist list</a>.  If you attended and aren&#8217;t on the list please <a href="http://twitter.com/direct_messages/create/yegrb">DM @yegrb</a> to get added.</p>
<h2>Announcements</h2>
<p>Once everyone was introduced we asked the room for any recent Ruby related news.</p>
<ul>
<li><a href="http://weblog.rubyonrails.org/2010/2/5/rails-3-0-beta-release">Rails</a> <a href="http://guides.rails.info/3_0_release_notes.html">3</a> <a href="http://railscasts.com/episodes/200-rails-3-beta-and-rvm">Beta</a> <a href="http://rvm.beginrescueend.com/">is</a> <a href="http://www.rubyinside.com/rails-3-0-beta-links-2966.html">out</a>!</li>
<li><a href="http://github.com/mynyml/harmony">Harmony</a> lets you test your javascript in your Ruby tests.</li>
<li><a href="http://www.yardsticksoftware.com/">Yardstick Software</a> is looking for Rails developers.  <a href="http://twitter.com/direct_messages/create/donriep">DM @DonRiep</a> for details.</li>
</ul>
<h2>Talks</h2>
<p>Next came the speakers.  There was a range of topics, from intermediate to advanced but all really interesting and great to see coming from local developers.</p>
<ul>
<li>&#8220;<a href="http://blog.darkhax.com/talks">Fly Your HTTP To The Moon</a>&#8221; by Daniel (<a href="http://twitter.com/darkhelmetlive">@darkhelmetlive</a>)</li>
<li>&#8220;<a href="http://markbennett.ca/2010/03/using-riak-with-ruby/">So You Built The Next FarmVille (Riak with Ruby)</a>&#8221; by Mark (<a href="http://twitter.com/MarkBennett">@MarkBennett</a>)</li>
<li>&#8220;<a href="http://oncloud.org/">oncloud.org</a>&#8221; by Graham (<a href="http://twitter.com/stormbrew">@stormbrew</a>)</li>
<li>&#8220;What&#8217;s your stack?&#8221; by Mike (<a href="http://twitter.com/mikedeering">@mikedeering</a>)
<ul>
<li>Editors &#8211;&gt; <a href="http://macromates.com/">TextMate</a>, <a href="http://www.gnu.org/software/emacs/">emacs</a>, <a href="http://www.vim.org/">vim</a></li>
<li>Testing &#8211;&gt; <a href="http://github.com/thoughtbot/shoulda">shoulda</a>, <a href="http://wiki.rubyonrails.org/testing/remarkable">remarkable</a>, <a href="http://rspec.info/">rspec</a>, <a href="http://www.zenspider.com/ZSS/Products/ZenTest/">ZenTest</a>, <a href="http://robots.thoughtbot.com/post/159807023/waiting-for-a-factory-girl">factorygirl</a>, <a href="http://github.com/notahat/machinist">machinist</a>, <a href="http://faker.rubyforge.org/">Faker</a></li>
<li>Continous Integration &#8211;&gt; <a href="http://github.com/thoughtworks/cruisecontrol.rb">cruisecontrol.rb</a>, <a href="http://integrityapp.com/">integrity</a></li>
<li>Metrics &#8211;&gt; <a href="http://www.brynary.com/2007/9/13/scourging-your-ruby-code-with-flog">flog</a>, <a href="http://metric-fu.rubyforge.org/">MetricFu</a>, <a href="http://getcaliper.com/">http://getcaliper.com/</a></li>
<li>Database &#8211;&gt; <a href="http://www.mysql.com/">MySQL </a>(<a href="http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html">on strict mode</a>), <a href="http://github.com/sdsykes/slim_scrooge">slim_scrooge</a>, <a href="http://riak.basho.com/">Riak</a></li>
<li>View &#8211;&gt; <a href="http://www.blueprintcss.org/">Blueprint</a>, <a href="http://compass-style.org/">compass</a>, <a href="http://haml-lang.com/">HAML</a>, <a href="http://sass-lang.com/">SASS</a>, <a href="http://typekit.com/">Typekit</a>, <a id="kstc" title="cufon" href="http://cufon.shoqolate.com/generate/">cufon</a></li>
<li>Deployment &#8211;&gt; <a href="http://rake.rubyforge.org/">rake</a>, <a href="http://synthesis.sbecker.net/pages/asset_packager">asset_packeger</a>, <a href="http://www.railsinside.com/plugins/354-jammit-industrial-strength-asset-packaging-for-rails-apps.html">jammit</a></li>
<li>Stacks &#8211;&gt; <a href="http://rubyonrails.org/">Rails</a>, <a href="http://www.sinatrarb.com/">Sinatra</a>, <a href="http://rack.rubyforge.org/">Rack</a>, <a href="http://coderack.org/">coderack.org</a></li>
<li>Distribution &#8211;&gt; <a href="http://github.com/carlhuda/bundler">bundler</a>, <a href="http://rubygems.org/">rubygems</a></li>
<li>Hosting &#8211;&gt; <a href="http://heroku.com/">Heroku</a>, <a href="http://oncloud.org/">oncloud.org</a></li>
<li>Authentication &#8211;&gt; <a href="http://github.com/binarylogic/authlogic">authlogic</a></li>
<li>Comet &#8211;&gt; <a href="http://juggernaut.rubyforge.org/">juggernaut</a>, <a href="http://nodejs.org/">node.js</a>, <a href="http://www.tornadoweb.org/">tornado</a></li>
<li>Issue Tracking &#8211;&gt; <a href="http://lighthouseapp.com/">lighthouse</a>, <a href="http://www.fogcreek.com/fogbugz/">fogbugs</a>, <a href="http://www.redmine.org/">redmine</a>, <a href="http://trac.edgewall.org/">Trac</a></li>
<li>Misc &#8211;&gt; <a href="http://staticmatic.rubyforge.org/">staticmatic</a>, <a href="http://github.com/jlong/serve">serve</a></li>
</ul>
</li>
</ul>
<p><strong>Feedback</strong></p>
<p>We got a little feedback from attendees last night, but we will be putting together a quick survey to see what people thought of our first meetup.  Watch <a href="http://twitter.com/yegrb">@yegrb</a> for the link.</p>
<p>There was some interest from specific attendees in getting together to hack on a website for the meetups.  If you&#8217;re interested in helping out ping <a href="http://tinyurl.com/yzvjx3l">@yegrb</a> on Twitter.</p>
<h2>Wrap Up</h2>
<p>The meetup finished up a little before 9:00 when everyone cleared out of the offices and some people headed over to Pub 1905.</p>
<p>Overall, the meetup was a great success.  We had lots of people who weren&#8217;t able to make it out this time, and there are already people talking about what to do at the next meetup.  The plan is to hold another meetup in about a month, though the date and location are still TBD.</p>
<p>Thanks again to Nexopia for the space, all the speakers, and everyone who came out!</p>
]]></content:encoded>
			<wfw:commentRss>http://markbennett.ca/2010/03/edmontons-first-ruby-meetup/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Using Riak with Ruby</title>
		<link>http://markbennett.ca/2010/03/using-riak-with-ruby/</link>
		<comments>http://markbennett.ca/2010/03/using-riak-with-ruby/#comments</comments>
		<pubDate>Wed, 03 Mar 2010 16:55:23 +0000</pubDate>
		<dc:creator>Mark</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[activeresource]]></category>
		<category><![CDATA[edmonton]]></category>
		<category><![CDATA[riak]]></category>
		<category><![CDATA[ripple]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[yegrb]]></category>

		<guid isPermaLink="false">http://markbennett.ca/?p=341</guid>
		<description><![CDATA[Last night I gave a talk on how to use Ruby with Riak.  I used @seancribbs excellent Ripple gem to get them playing nicely together.  The talk is embedded after the jump&#8230;


]]></description>
			<content:encoded><![CDATA[<p>Last night I gave a talk on how to use Ruby with <a href="http://riak.basho.com/">Riak</a>.  I used <a href="http://twitter.com/seancribbs">@seancribbs</a> excellent <a href="http://github.com/seancribbs/ripple">Ripple </a>gem to get them playing nicely together.  The talk is embedded after the jump&#8230;</p>
<p><span id="more-341"></span></p>
<p><iframe src="http://docs.google.com/present/embed?id=dgqhgd43_29dsw2n2db&#038;interval=5&#038;size=m" frameborder="0" width="555" height="451"></iframe></p>
]]></content:encoded>
			<wfw:commentRss>http://markbennett.ca/2010/03/using-riak-with-ruby/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Results of the Edmonton Ruby Meetup Survey</title>
		<link>http://markbennett.ca/2010/02/results-of-the-edmonton-ruby-meetup-survey/</link>
		<comments>http://markbennett.ca/2010/02/results-of-the-edmonton-ruby-meetup-survey/#comments</comments>
		<pubDate>Mon, 01 Feb 2010 22:10:58 +0000</pubDate>
		<dc:creator>Mark</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://markbennett.ca/?p=322</guid>
		<description><![CDATA[To start, thank-you to everyone that has taken part in our survey.  We&#8217;ve had a great response, and appreciate you taking the time to answer our questions.  Your responses are key in figuring out the details and audience of our meetups.   And now the results&#8230;
When we look at the responses it&#8217;s clear that  most people are [...]]]></description>
			<content:encoded><![CDATA[<p>To start, thank-you to everyone that has taken part in our survey.  We&#8217;ve had a great response, and appreciate you taking the time to answer our questions.  Your responses are key in figuring out the details and audience of our meetups.   And now the results&#8230;</p>
<p>When we look at the responses it&#8217;s clear that  <span style="text-decoration: underline;">most people are interested in meeting once a month for lighting talks and informal hacking sessions</span>, starting sometime in the next month.  There is also some interest in longer more formal talks and tutorials, so they&#8217;ll probably have a place in our meetups at some point too.</p>
<p>Based on these results we&#8217;re shooting to start meetings by the end of February and will have more details once a date and time are confirmed.  If you haven&#8217;t had a chance to answer the survey polling is still open.   Have your say at <a href="http://edmontonrb.org">http://edmontonrb.org</a>.</p>
<p>For the statistically inclined keep reading for all the results.  (These charts will update in real time.)</p>
<h3>What format would you like to see the gatherings take on?</h3>
<div><img class="aligncenter" title="Meeting Format" src="http://spreadsheets.google.com/a/edmontonrb.org/oimg?key=0AoGeLLyVIxPTdHJSZVk3X21CaGdrNWdBV01RWmhpbUE&amp;oid=4&amp;v=1265060857823" alt="This chart shows users answers to the question, &quot;What format would you like the meetings to take&quot;.  Lightning talks, and hacking and informal session are most popular, followed by formal hour-long presentations." width="800" height="600" /></p>
<h3>How often would you like to meet?</h3>
</div>
<div><img class="aligncenter" title="How often would you like to meet?" src="http://spreadsheets.google.com/a/edmontonrb.org/oimg?key=0AoGeLLyVIxPTdHJSZVk3X21CaGdrNWdBV01RWmhpbUE&amp;oid=6&amp;v=1265061082503" alt="By far the majority of those surveyed would like to meet once a month." width="800" height="600" /></p>
<h3>When would you like the meetings to start?</h3>
</div>
<div><img class="aligncenter" title="When would you like the meet-ups to start?" src="http://spreadsheets.google.com/a/edmontonrb.org/oimg?key=0AoGeLLyVIxPTdHJSZVk3X21CaGdrNWdBV01RWmhpbUE&amp;oid=8&amp;v=1265061616058" alt="Seems like people would like them to start soon!" width="800" height="600" /></div>
]]></content:encoded>
			<wfw:commentRss>http://markbennett.ca/2010/02/results-of-the-edmonton-ruby-meetup-survey/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>HELP ME: I&#8217;m running 14km to support the RSPCA</title>
		<link>http://markbennett.ca/2009/07/help-me-im-running-14km-to-support-the-rspca/</link>
		<comments>http://markbennett.ca/2009/07/help-me-im-running-14km-to-support-the-rspca/#comments</comments>
		<pubDate>Tue, 14 Jul 2009 03:31:11 +0000</pubDate>
		<dc:creator>Mark</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://markbennett.ca/?p=318</guid>
		<description><![CDATA[As you may know, I&#8217;m going to be doing a race called the City to Surf this August.  It&#8217;s 14km and I&#8217;ve convinced myself doing it won&#8217;t kill me (though it may shave a few years off!)  The race is setup so you can choose a charity to support, and I&#8217;ve selected the [...]]]></description>
			<content:encoded><![CDATA[<p>As you may know, I&#8217;m going to be doing a race called the <a href="http://www.city2surf.com.au">City to Surf </a>this August.  It&#8217;s 14km and I&#8217;ve convinced myself doing it won&#8217;t kill me (though it may shave a few years off!)  The race is setup so you can choose a charity to support, and I&#8217;ve selected the RSPCA.</p>
<p>As long-distance doggy daddies we&#8217;ve both been thinking a lot about the welfare of our pet, and know that he&#8217;s been well taken care of.  We also know that not all pets are as lucky as Bruno.  The RSPCA helps thousands of animals in need every year, and we thought supporting it would be a great way to honour Bruno while we&#8217;re away.</p>
<p>If you&#8217;re interested in sponsoring me I&#8217;ve got a page setup at:</p>
<p><a href="http://city2surf.everydayhero.com.au/mark_bennett">http://city2surf.everydayhero.com.au/mark_bennett</a></p>
<p><object width="425" height="344" data="http://www.youtube.com/v/chTs16XQs6M&amp;hl=en&amp;fs=1&amp;rel=0" type="application/x-shockwave-flash"><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="src" value="http://www.youtube.com/v/chTs16XQs6M&amp;hl=en&amp;fs=1&amp;rel=0" /><param name="allowfullscreen" value="true" /></object></p>
]]></content:encoded>
			<wfw:commentRss>http://markbennett.ca/2009/07/help-me-im-running-14km-to-support-the-rspca/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Discovering services using Bonjour, DNSSD, and Ruby</title>
		<link>http://markbennett.ca/2009/06/discovering-services-using-bonjour-dnssd-and-ruby/</link>
		<comments>http://markbennett.ca/2009/06/discovering-services-using-bonjour-dnssd-and-ruby/#comments</comments>
		<pubDate>Wed, 03 Jun 2009 07:10:06 +0000</pubDate>
		<dc:creator>Mark</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[dnssd]]></category>
		<category><![CDATA[howto]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[ubuntu]]></category>
		<category><![CDATA[zeroconf]]></category>

		<guid isPermaLink="false">http://markbennett.ca/?p=278</guid>
		<description><![CDATA[This is the third part in a series of articles I&#8217;ve been writing on using Zeroconf (aka Bonjour) in Ruby.  The first post covered Zeroconf and how to install DNSSD, the most popular Ruby Zeroconf libary. The second post went through creating and registering a web service using Zeroconf so other machines on your network [...]]]></description>
			<content:encoded><![CDATA[<p>This is the third part in a series of articles I&#8217;ve been writing on using Zeroconf (aka Bonjour) in Ruby.  The first post covered <a href="http://markbennett.ca/2009/05/zeroconf-and-dnssd/">Zeroconf and how to install DNSSD</a>, the most popular Ruby Zeroconf libary. The second post went through <a href="http://markbennett.ca/2009/05/registering-a-zeroconf-service/">creating and registering a web service using Zeroconf</a> so other machines on your network can find it.</p>
<p>In this post we&#8217;ll digress a bit and write a command-line tool to monitor and explore the information Zeroconf broadcasts on your network.  This will give us some background for the next post when we modify our web server to list other servers of a similar kind running on your network.  If you&#8217;re already familiar with how to browse and retrieve information about Zeroconf services using DNSSD then you may want to skip ahead.<span id="more-278"></span></p>
<p>Before I get too carried away, there are just a few notes about the require statements I&#8217;ll use in my examples.  As with all Ruby Zeroconf clients I will by requiring the DNSSD library.  Older versions of DNSSD seem to have compatibility issues when running on Linux so I always require DNSSD 0.7.1 (the latest at the time of this writing.)</p>
<p>We&#8217;re now ready to start browsing the network for Zeroconf services.  DNSSD supports browsing the network using either a synchronous or asynchronous API.  The synchronous API is new in version 0.7.1 and has the disadvantage that using it locks up your main application thread while it searches the network.  I haven&#8217;t used it in my applications and will only be covering the asynchronous API in this tutorial.  See the <a href="http://markbennett.ca/code/doc/dnssd-0.7.1/rdoc/">DNSSD 0.7.1 rdoc</a> for an <a href="http://markbennett.ca/code/doc/dnssd-0.7.1/rdoc/classes/DNSSD.html#M000006">example of using the synchronous browsing API</a>.</p>
<p><a href="http://markbennett.ca/code/doc/dnssd-0.7.1/rdoc/classes/DNSSD.html#M000005">DNSSD.browse()</a> is the core function to understand when browsing the network using DNSSD.  Given a Zeroconf service type  DNSSD.browse() will provide a <a href="http://markbennett.ca/code/doc/dnssd-0.7.1/rdoc/classes/DNSSD/Reply.html">DNSSD::Reply</a> to a block of code which will be called each time a service is identified on the network.  It also returns a reference to a <a href="http://markbennett.ca/code/doc/dnssd-0.7.1/rdoc/classes/DNSSD/Service.html">DNSSD::Service</a> object which can be used to control the and stop the browsing.</p>
<p>For example let&#8217;s look at the following code:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
</pre></td><td class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'rubygems'</span>
gem <span style="color:#996600;">'dnssd'</span>, <span style="color:#996600;">'0.7.1'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'dnssd'</span>
&nbsp;
<span style="color:#008000; font-style:italic;"># Browse the network for web servers (_http._tcp services)</span>
<span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;Scanning network for web servers&quot;</span>
service = DNSSD.<span style="color:#9900CC;">browse</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'_http._tcp.'</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>reply<span style="color:#006600; font-weight:bold;">|</span>
 <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;received: #{reply.inspect}&quot;</span>
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
<span style="color:#008000; font-style:italic;"># Catch the interrupt signal</span>
interrupted = <span style="color:#0000FF; font-weight:bold;">false</span>
<span style="color:#CC00FF; font-weight:bold;">Signal</span>.<span style="color:#CC0066; font-weight:bold;">trap</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">&quot;INT&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  interrupted = <span style="color:#0000FF; font-weight:bold;">true</span>
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
<span style="color:#008000; font-style:italic;"># Run until the application is interrupted</span>
<span style="color:#CC0066; font-weight:bold;">loop</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  <span style="color:#9966CC; font-weight:bold;">if</span> interrupted
    <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;Halting network scan&quot;</span>
    service.<span style="color:#9900CC;">stop</span>
    <span style="color:#CC0066; font-weight:bold;">exit</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></td></tr></table></div>

<p>On line 7, we call DNSSD.browse() and get back a reference to the DNSSD::Service object we can use to control the browsing.  We pass into DNSSD.browse() the kind of Zeroconf service we&#8217;re looking for (_http._tcp) along with a block of code which will print out the details of each service we find.  Inside that block of code we can access the service details using the reply object which is an instance of DNSSD::Reply.</p>
<p>The rest of the code in this example just runs the application until the user presses CTRL+C which sends it an interrupt signal which will terminate the application.  The application clean-up is handled on lines 20-22.  Notice the call to service.stop on line 21.  This tells the DNSSD::Service we got back on line 7 to stop running as we&#8217;re done with it.</p>
<p>Save and run this code, then try running your strawberry ice cream web service from <a href="http://markbennett.ca/2009/05/registering-a-zeroconf-service/">the previous post</a> at the same time in another window. You should see a message like:</p>
<pre>received: #&lt;DNSSD::Reply Strawberry\032Ice\032Cream\032\0431._http._tcp.local&gt;</pre>
<p>If you stop the web service you&#8217;ll see the message:</p>
<pre>received: #&lt;DNSSD::Reply Strawberry\032Ice\032Cream\032\0431._http._tcp.local&gt;</pre>
<p>Huh? That&#8217;s not what we expected! Why do we see the web server again when it&#8217;s shutting down?</p>
<p>It turns out that Zeroconf services broadcast when registering and then again when shutting down.  We can use the <a href="http://markbennett.ca/code/doc/dnssd-0.7.1/rdoc/classes/DNSSD/Reply.html">DNSSD::Reply</a>&#8217;s flags attribute to determine whether the service is being added or should be removed.  The flags attribute is an instance of <a href="http://markbennett.ca/code/doc/dnssd-0.7.1/rdoc/classes/DNSSD/Flags.html">DNSSD::Flags</a> and provides details about the state of the Zeroconf service.  Let&#8217;s modify our code to print out the flags it receives with each broadcast:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
</pre></td><td class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'rubygems'</span>
gem <span style="color:#996600;">'dnssd'</span>, <span style="color:#996600;">'0.7.1'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'dnssd'</span>
&nbsp;
<span style="color:#008000; font-style:italic;"># Browse the network for web servers (_http._tcp services)</span>
<span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;Scanning network for web servers&quot;</span>
service = DNSSD.<span style="color:#9900CC;">browse</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'_http._tcp.'</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>reply<span style="color:#006600; font-weight:bold;">|</span>
 <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;received: #{reply.inspect}&quot;</span>
 <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;<span style="color:#000099;">\t</span>flags: #{reply.flags.inspect}&quot;</span>
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
<span style="color:#008000; font-style:italic;"># Catch the interrupt signal</span>
interrupted = <span style="color:#0000FF; font-weight:bold;">false</span>
<span style="color:#CC00FF; font-weight:bold;">Signal</span>.<span style="color:#CC0066; font-weight:bold;">trap</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">&quot;INT&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  interrupted = <span style="color:#0000FF; font-weight:bold;">true</span>
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
<span style="color:#008000; font-style:italic;"># Run until the application is interrupted</span>
<span style="color:#CC0066; font-weight:bold;">loop</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  <span style="color:#9966CC; font-weight:bold;">if</span> interrupted
    <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;Halting network scan&quot;</span>
    service.<span style="color:#9900CC;">stop</span>
    <span style="color:#CC0066; font-weight:bold;">exit</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></td></tr></table></div>

<p>Run this new code in one window and try starting and stopping your web server in another.  You should now see a message which looks like this when the web server starts:</p>
<pre>received: #&lt;DNSSD::Reply Strawberry\032Ice\032Cream\032\0431._http._tcp.local&gt;
    flags: #&lt;DNSSD::Flags add&gt;</pre>
<p>and like this when the web server stops:</p>
<pre>received: #&lt;DNSSD::Reply Strawberry\032Ice\032Cream\032\0431._http._tcp.local&gt;
    flags: #&lt;DNSSD::Flags&gt;</pre>
<p>As you can see new services have the DNSSD::Flags::Add constant set.  Services that are being shutdown don&#8217;t have any flags.</p>
<p>With this in mind let&#8217;s modify our code one more time to show when services are added and removed.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
</pre></td><td class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'rubygems'</span>
gem <span style="color:#996600;">'dnssd'</span>, <span style="color:#996600;">'0.7.1'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'dnssd'</span>
&nbsp;
<span style="color:#008000; font-style:italic;"># Browse the network for web servers (_http._tcp services)</span>
<span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;Scanning network for web servers&quot;</span>
service = DNSSD.<span style="color:#9900CC;">browse</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'_http._tcp.'</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>reply<span style="color:#006600; font-weight:bold;">|</span>
  <span style="color:#9966CC; font-weight:bold;">if</span> <span style="color:#006600; font-weight:bold;">&#40;</span>reply.<span style="color:#9900CC;">flags</span> == <span style="color:#6666ff; font-weight:bold;">DNSSD::Flags::Add</span><span style="color:#006600; font-weight:bold;">&#41;</span>
      <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;adding: #{reply.inspect}&quot;</span>
  <span style="color:#9966CC; font-weight:bold;">else</span>
      <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;removing: #{reply.inspect}&quot;</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
<span style="color:#008000; font-style:italic;"># Catch the interrupt signal</span>
interrupted = <span style="color:#0000FF; font-weight:bold;">false</span>
<span style="color:#CC00FF; font-weight:bold;">Signal</span>.<span style="color:#CC0066; font-weight:bold;">trap</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">&quot;INT&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  interrupted = <span style="color:#0000FF; font-weight:bold;">true</span>
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
<span style="color:#008000; font-style:italic;"># Run until the application is interrupted</span>
<span style="color:#CC0066; font-weight:bold;">loop</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  <span style="color:#9966CC; font-weight:bold;">if</span> interrupted
    <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;Halting network scan&quot;</span>
    service.<span style="color:#9900CC;">stop</span>
    <span style="color:#CC0066; font-weight:bold;">exit</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></td></tr></table></div>

<p>Run this code again and try starting and stopping your web server.  Now instead of some cryptic flags you should see a friendly message indicating when services are being added and removed!</p>
<p>The last thing I&#8217;ll talk about in this post is how to resolve the the address of the web server from the DNSSD::Reply.  If you&#8217;ve read the DNSSD rdoc already you may have noticed that DNSSD::Reply has attributes we haven&#8217;t used yet.  The reason we haven&#8217;t used them is because most of them aren&#8217;t initialised when you get a reply back from the browse() method.  Instead the reply we get back has just enough information to tell us if the service is new, and for us to use the <a href="http://markbennett.ca/code/doc/dnssd-0.7.1/rdoc/classes/DNSSD.html#M000007">DNSSD.resolve()</a> method to get all it&#8217;s details.</p>
<p>Like DNSSD.browse() the DNSSD.resolve() method is asynchronous and passes a DNSSD::Reply into a block when it&#8217;s done executing.  The DNSSD::Reply we get back from resolve() is fully-populated and gives us access to things like the target (host name) where the service is running.</p>
<p>Let&#8217;s look at a final example which will show us a complete example of how to browse for web services on your network using Ruby and Zeroconf.  The changes happen on line&#8217;s 11-25.  We&#8217;re now taking the reply from the DNSSD.browse() method and using it&#8217;s name, type, and domain to resolve the details of the service in a new thread.  Since <a href="http://blog.headius.com/2008/02/rubys-threadraise-threadkill-timeoutrb.html">Thread#kill and timeout.rb are broken</a> we&#8217;ll let the browsing thread sleep for a few seconds and then use the resolver service handle to stop the resolver whether it&#8217;s found any details or not.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
</pre></td><td class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'rubygems'</span>
gem <span style="color:#996600;">'dnssd'</span>, <span style="color:#996600;">'0.7.1'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'dnssd'</span>
&nbsp;
<span style="color:#008000; font-style:italic;"># Browse the network for web servers (_http._tcp services)</span>
<span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;Scanning network for web servers&quot;</span>
service = DNSSD.<span style="color:#9900CC;">browse</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'_http._tcp.'</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>reply<span style="color:#006600; font-weight:bold;">|</span>
  <span style="color:#9966CC; font-weight:bold;">if</span> <span style="color:#006600; font-weight:bold;">&#40;</span>reply.<span style="color:#9900CC;">flags</span> == <span style="color:#6666ff; font-weight:bold;">DNSSD::Flags::Add</span><span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;adding: #{reply.inspect}&quot;</span>
&nbsp;
    <span style="color:#008000; font-style:italic;"># Let's lookup the details</span>
    resolver_service = DNSSD.<span style="color:#9900CC;">resolve</span><span style="color:#006600; font-weight:bold;">&#40;</span>reply.<span style="color:#9900CC;">name</span>, reply.<span style="color:#9900CC;">type</span>, reply.<span style="color:#9900CC;">domain</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>resolved<span style="color:#006600; font-weight:bold;">|</span>
      <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;<span style="color:#000099;">\t</span>domain = #{resolved.domain}&quot;</span>
      <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;<span style="color:#000099;">\t</span>flags = #{resolved.flags.inspect}&quot;</span>
      <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;<span style="color:#000099;">\t</span>fullname = #{resolved.fullname}&quot;</span>
      <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;<span style="color:#000099;">\t</span>interface = #{resolved.interface}&quot;</span>
      <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;<span style="color:#000099;">\t</span>name = #{resolved.name}&quot;</span>
      <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;<span style="color:#000099;">\t</span>port = #{resolved.port}&quot;</span>
      <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;<span style="color:#000099;">\t</span>service = #{resolved.service}&quot;</span>
      <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;<span style="color:#000099;">\t</span>target = #{resolved.target}&quot;</span>
      <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;<span style="color:#000099;">\t</span>text_record = #{resolved.text_record.inspect}&quot;</span>
      <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;<span style="color:#000099;">\t</span>type = #{resolved.type}&quot;</span>
    <span style="color:#9966CC; font-weight:bold;">end</span>
    <span style="color:#CC0066; font-weight:bold;">sleep</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#006666;">2</span><span style="color:#006600; font-weight:bold;">&#41;</span>
    resolver_service.<span style="color:#9900CC;">stop</span>
  <span style="color:#9966CC; font-weight:bold;">else</span>
      <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;removing: #{reply.inspect}&quot;</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
<span style="color:#008000; font-style:italic;"># Catch the interrupt signal</span>
interrupted = <span style="color:#0000FF; font-weight:bold;">false</span>
<span style="color:#CC00FF; font-weight:bold;">Signal</span>.<span style="color:#CC0066; font-weight:bold;">trap</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">&quot;INT&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  interrupted = <span style="color:#0000FF; font-weight:bold;">true</span>
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
<span style="color:#008000; font-style:italic;"># Run until the application is interrupted</span>
<span style="color:#CC0066; font-weight:bold;">loop</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  <span style="color:#9966CC; font-weight:bold;">if</span> interrupted
    <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;Halting network scan&quot;</span>
    service.<span style="color:#9900CC;">stop</span>
    <span style="color:#CC0066; font-weight:bold;">exit</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></td></tr></table></div>

<p>And that&#8217;s it!  You&#8217;re using ruby and Zeroconf like a pro.  Let&#8217;s hope you remember all this because we&#8217;ll use it in the next post to find out what kinds of ice cream the other people on your network enjoy!</p>
]]></content:encoded>
			<wfw:commentRss>http://markbennett.ca/2009/06/discovering-services-using-bonjour-dnssd-and-ruby/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Registering a service using DNSSD and Zeroconf</title>
		<link>http://markbennett.ca/2009/05/registering-a-zeroconf-service/</link>
		<comments>http://markbennett.ca/2009/05/registering-a-zeroconf-service/#comments</comments>
		<pubDate>Sun, 24 May 2009 03:39:27 +0000</pubDate>
		<dc:creator>Mark</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[dnssd]]></category>
		<category><![CDATA[howto]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[ubuntu]]></category>
		<category><![CDATA[zeroconf]]></category>

		<guid isPermaLink="false">http://markbennett.ca/?p=266</guid>
		<description><![CDATA[In this series of posts I&#8217;m using the power of Zeroconf and DNSSD to share my strawberry ice cream advocacy web site with the rest of the network. In the last post we learned all about Zeroconf and how to install DNSSD so you can use Zeroconf from Ruby.
In this post I&#8217;ll put DNSSD to [...]]]></description>
			<content:encoded><![CDATA[<p>In this series of posts I&#8217;m using the power of Zeroconf and DNSSD to share my strawberry ice cream advocacy web site with the rest of the network. In the last post we learned all about <a href="http://markbennett.ca/2009/05/zeroconf-and-dnssd/">Zeroconf and how to install DNSSD so you can use Zeroconf from Ruby</a>.</p>
<p>In this post I&#8217;ll put DNSSD to work and we&#8217;ll use it to register our web site on the network.</p>
<p><span id="more-266"></span></p>
<p>To begin let&#8217;s go back and look at my web site code:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'rubygems'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'sinatra'</span>
&nbsp;
get <span style="color:#996600;">'/'</span> <span style="color:#9966CC; font-weight:bold;">do</span>
 <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;I love Strawberry ice cream!&quot;</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>You can run your Sinatra application by saving this file as sp1.rb typing:</p>
<pre>ruby sp1.rb</pre>
<p>By default Sinatra will run on port 4567.  To specify your own port stop the web server by pressing CTRL+C and start it again using:</p>
<pre>ruby sp1.rb -p 12345</pre>
<p>This will start Sinatra on port 12345, but you can use any port you want.</p>
<p>Now that we know how to run Sinatra we need to register our service using Zeroconf.</p>
<p>I&#8217;ll begin by requiring DNSSD.  I like to require a specific version since I&#8217;ve had trouble compiling older versions of the gem on Linux:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;">gem <span style="color:#996600;">'dnssd'</span>, <span style="color:#996600;">'0.7.1'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'dnssd'</span></pre></div></div>

<p>Now we&#8217;ll add some lines right before we start the Sinatra application to register the web server:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;">register_service =
  DNSSD.<span style="color:#9900CC;">register</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">&quot;strawberry ice cream +1&quot;</span>, <span style="color:#996600;">&quot;_http._tcp&quot;</span>, <span style="color:#0000FF; font-weight:bold;">nil</span>, <span style="color:#6666ff; font-weight:bold;">Sinatra::Application</span>.<span style="color:#9900CC;">port</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">do</span>
    <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;* Registered the strawberry web server&quot;</span>
  <span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>It&#8217;s good form to keep a reference to the register service around so you can unregister it manually using:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;">register_service.<span style="color:#9900CC;">stop</span></pre></div></div>

<p>Normally you won&#8217;t need to worry about this since DNSSD will unregister the service automatically when the Sinatra application terminates and the register_service object is cleaned up.</p>
<p>The final code should look like this:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'rubygems'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'sinatra'</span>
gem <span style="color:#996600;">'dnssd'</span>, <span style="color:#996600;">'0.7.1'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'dnssd'</span>
&nbsp;
get <span style="color:#996600;">'/'</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  <span style="color:#996600;">&quot;I love strawberry ice cream!&quot;</span>
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
register_service =
  DNSSD::register<span style="color:#006600; font-weight:bold;">&#40;</span>
      <span style="color:#996600;">&quot;Strawberry Ice Cream +1&quot;</span>,
      <span style="color:#996600;">&quot;_http._tcp.&quot;</span>, <span style="color:#0000FF; font-weight:bold;">nil</span>,
      <span style="color:#6666ff; font-weight:bold;">Sinatra::Application</span>.<span style="color:#9900CC;">port</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">do</span>
    <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;* Registering the strawberry web server&quot;</span>
  <span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>If you&#8217;re on a Mac or a PC running Safari and you want to see the website on your browser, just open your Safari Preferences (Safair &gt; Preferences) and then check off the Bonjour boxes under the Bookmarks preferences panel.  Close the Preferences pane and you should see a Bonjour folder on your bookmarks bar with an entry for &#8220;strawberry ice cream +1&#8243;.</p>
<p>Non-Safari users on Ubuntu, Mac OS X, and Windows can follow the directions Andrew TJ&#8217;s site to install the <a href="http://andrew.tj.id.au/projects/bonjourfoxy/">BonjourFoxy</a> Firefox addon which will show Zeroconf web servers on your network.</p>
<div id="attachment_271" class="wp-caption aligncenter" style="width: 621px"><img class="size-full wp-image-271" title="bonjourfoxy_041_small" src="http://markbennett.ca/wp-content/uploads/2009/05/bonjourfoxy_041_small.png" alt="Original Source: http://andrew.tj.id.au/projects/bonjourfoxy/" width="611" height="281" /><p class="wp-caption-text">Original Source: http://andrew.tj.id.au/projects/bonjourfoxy/</p></div>
<p>And that&#8217;s it, you&#8217;re now sharing your love of strawberry ice cream with the whole network!</p>
<p>In my next post I&#8217;ll talk about how to write Ruby code to automatically discover and list other strawberry ice cream lovers on your network.</p>
]]></content:encoded>
			<wfw:commentRss>http://markbennett.ca/2009/05/registering-a-zeroconf-service/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Strawberry advocacy with Zeroconf and DNSSD</title>
		<link>http://markbennett.ca/2009/05/zeroconf-and-dnssd/</link>
		<comments>http://markbennett.ca/2009/05/zeroconf-and-dnssd/#comments</comments>
		<pubDate>Sun, 24 May 2009 03:09:18 +0000</pubDate>
		<dc:creator>Mark</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[dnssd]]></category>
		<category><![CDATA[howto]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[ubuntu]]></category>
		<category><![CDATA[zeroconf]]></category>

		<guid isPermaLink="false">http://markbennett.ca/?p=240</guid>
		<description><![CDATA[
Strawberry ice cream has a bad rep.
It&#8217;s not as sexy as chocolate and not as safe as plain vanilla.  Instead it&#8217;s immediately relegated to third place in most ice cream debates.
Not satisfied with this situation I did what any coder would do and created a web site to evangalize strawberry ice cream using Sinatra.

Sinatra [...]]]></description>
			<content:encoded><![CDATA[<p><img class="size-full wp-image-257 alignright" title="strawberry_small" src="http://markbennett.ca/wp-content/uploads/2009/05/strawberry_small.png" alt="Strawberry Ice Cream" width="197" height="300" /></p>
<p>Strawberry ice cream has a bad rep.</p>
<p>It&#8217;s not as sexy as chocolate and not as safe as plain vanilla.  Instead it&#8217;s immediately relegated to third place in most ice cream debates.</p>
<p>Not satisfied with this situation I did what any coder would do and created a web site to evangalize strawberry ice cream using <a href="http://www.sinatrarb.com/">Sinatra</a>.</p>
<p><span id="more-240"></span><br />
Sinatra is great platform for quickly creating and deploying web applications, and works well for this example since the whole web site can be created with the following lines of code:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'rubygems'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'sinatra'</span>
&nbsp;
get <span style="color:#996600;">'/'</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  <span style="color:#996600;">'I love Strawberry ice cream!'</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>Now that I&#8217;ve got it running though, I realised it&#8217;s not going to do me much good if no one can find it.  I could email a link to everyone or post it on Facebook or Twitter, but I want something that will work automatically and even if I don&#8217;t have access to the Internet.  You may laugh and wonder how often that will be a problem but it&#8217;s something I ran into just this past weekend while at RailsCamp5 in the middle of the Australian hills.  Cut off from the rest of the world we had no Internet and no tools to publish the web site.  Or did we?</p>
<p>Applications like iTunes and Safari are able to find other computers and the services they offer without using an Apple server or the Internet.  To test this for yourself just try sharing your iTunes music library with someone else on your network.  If you unplug the Internet from your router the computers can still see each other and still see each others shared music.  But how do they do this?</p>
<p>The answer is a magically technology called Zeroconf.</p>
<h2 class="western">Zeroconf</h2>
<p>Zeroconf is a way for computers to find the names and addresses of the other machines on their network and to register the services they provide without using a central server.  Zeroconf started life in the labs at Apple where it was originally called Rendezvous, until it was renamed <a href="http://www.apple.com/macosx/technology/bonjour.html">Bonjour</a> for copyright reasons. Apple originally released implementations of Bonjour for windows under a proprietary license which proved controvertial with the open source community.  This lead to the creation of the independent <a href="http://avahi.org/">Avahi</a> implementation which was released under a more liberal GPL license.  Since the creation of Avahi Apple has relicensed Bonjour under an Apache-style license, however Avahi remains the most popular implementation on most Linux distributions.  Interested readers should check out <a href="http://en.wikipedia.org/wiki/Zeroconf">Zeroconf&#8217;s Wikipedia entry</a> for all the dirt.</p>
<p>Zeroconf is a well supported technology and is available on Mac OS X, Windows, Linux.  There&#8217;s even support for the iPhone and Android phones.  It&#8217;s also got bindings or implementation written in C, C++, C#, Java, Python and Ruby.  Neat, eh?</p>
<h2 class="western">DNSSD</h2>
<p>To unleash the power of Zeroconf in Ruby we&#8217;ll be using a ruby gem called <a href="http://rubyforge.org/projects/dnssd/">DNSSD</a>.    This gem is a wrapper around Apple&#8217;s Bonjour library on Windows and Mac OS X.   On Linux DNSSD wraps Avahi&#8217;s Bonjour compatibility layer.  To develop using DNSSD you&#8217;ll need to make sure that you&#8217;ve installed these libraries or else DNSSD won&#8217;t compile.  Windows and Mac users can download the required file from the Apple website.  You&#8217;ll also need to install the developer tools on Mac OS X. I&#8217;d appreciate feedback from Windows users on what tools they&#8217;re using to develop with DNSSD.</p>
<p>Ubuntu users can install Avahi and it&#8217;s Bonjour compatibility layer by typing:</p>
<pre>sudo apt-get install avahi-daemon avahi-utils libavahi-client-dev \
  libavahi-common-dev libavahi-compat-libdnssd-dev libnss-mdns \
  avahi-autoip</pre>
<p>Download and install any dependencies these libraries require.  If you haven&#8217;t already you&#8217;ll also need to grab the Ubuntu build tools:</p>
<pre>sudo apt-get install build-essential</pre>
<p>Once you&#8217;ve got the required Bonjour or Avahi libraries installed you can get DNSSD by running:</p>
<pre>sudo gem install dnssd</pre>
<p>Assuming everything works you&#8217;ll see the gem install build the native extensions, and then install the DNSSD documentation into rdoc and ri.  I&#8217;ve tested this with DNSSD 0.7.1 and found it to work on Linux however I have had problems compiling DNSSD 0.6.0 in the past.  If you see errors while compiling you should make sure your gem repository is pointing to the latest gem repository available on rubyforge.</p>
<p>If you&#8217;ve made it this far you&#8217;ve now got DNSSD installed.  It&#8217;s worth taking a few seconds to start up your gem rdoc server:</p>
<pre>gem server</pre>
<p>and then browsing <a href="http://localhost:8808/doc_root/dnssd-0.7.1/rdoc/index.html">the DNSSD documentation</a>.  It&#8217;s a little outdated but most of the examples should work with only some minor modifcations.</p>
<p>In the next post we&#8217;ll <a href="http://markbennett.ca/2009/05/registering-a-zeroconf-service/">put Zeroconf to use by using DNSSD to register our strawberry server on the network</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://markbennett.ca/2009/05/zeroconf-and-dnssd/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>UPDATE: Bringing test driven development to Drupal 5</title>
		<link>http://markbennett.ca/2009/04/updated-bringing-tdd-to-drupal-5/</link>
		<comments>http://markbennett.ca/2009/04/updated-bringing-tdd-to-drupal-5/#comments</comments>
		<pubDate>Thu, 02 Apr 2009 04:23:04 +0000</pubDate>
		<dc:creator>Mark</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[software]]></category>
		<category><![CDATA[Spotlight]]></category>
		<category><![CDATA[tdd]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[testing]]></category>

		<guid isPermaLink="false">http://markbennett.ca/?p=219</guid>
		<description><![CDATA[A few days ago I made a post about modifying phpunit_setup.inc to enable test driven development on Drupal 5.  Since then a few things have happened.
Drupal TDD
The first big news is that phpunit_setup.inc is now part of a new project called Drupal_TDD.  To quote our README file the project is,
&#8220;&#8230;designed to make Test Driven Development [...]]]></description>
			<content:encoded><![CDATA[<p><img class="alignright size-full wp-image-238" title="Test Driven Development Badge" src="http://markbennett.ca/wp-content/uploads/2009/04/tdd-badge.png" alt="Test Driven Development Badge" width="106" height="106" />A few days ago I made a post about <a href="http://markbennett.ca/2009/03/bringing-tdd-to-drupal-5/">modifying phpunit_setup.inc to enable test driven development on Drupal 5</a>.  Since then a few things have happened.</p>
<h3>Drupal TDD</h3>
<p>The first big news is that phpunit_setup.inc is now part of a new project called<a href="http://github.com/patshaughnessy/drupal_tdd/"> Drupal_TDD</a>.  To quote our README file the project is,</p>
<blockquote><p>&#8220;&#8230;designed to make Test Driven Development with Drupal easy and enjoyable.&#8221;</p></blockquote>
<p>As part of this change the source code for phpunit_setup.inc has moved to <a href="http://github.com/">GitHub</a>.  This will allow us to collaborate more easily on phpunit_setup.inc in the future, and should make it easier for any readers hoping to give test driven development with Drupal a try.</p>
<p>Check out the <a href="http://github.com/patshaughnessy/drupal_tdd/">project page</a> for news and updates, or to download the latest version of phpunit_setup.inc.  You can also visit the <a href="http://wiki.github.com/patshaughnessy/drupal_tdd">Drupal TDD wiki</a> for documentation and examples.</p>
<h3>Good-bye Backports, Hello Automated Version Detection</h3>
<p>In other news, I&#8217;ve also been busy refactoring code to integrate some thoughts and feedback I&#8217;ve received.  The main changes are:</p>
<ol>
<li><strong>Using phpunit_setup.inc in Drupal 5 now works just like using it on Drupal 6.</strong> You no longer need to include drupal6_backports.inc in your unit test files.  The same steps Pat <a href="http://patshaughnessy.net/2009/1/19/drupal-test-database">outlines on his blog</a> to use phpunit_setup.inc in Drupal 6 will now work with Drupal 5.  This also means you won&#8217;t need to change your tests when migrating between versions.</li>
<li><strong>When running under Drupal 5 phpunit_setup.inc now creates the same database schema as running a fresh Drupal 5 install.</strong> Rather than backporting the Drupal 6 installation process to Drupal 5 which created tables you wouldn&#8217;t normally use in a Drupal 5 installation, phpunit_setup.inc now automatically detects the version of Drupal you&#8217;re using and runs the appropriate database installation procedure.</li>
</ol>
<p>There are still more announcements on the way, but that&#8217;s all we can reveal right now.  Stay tuned for further developments!</p>
]]></content:encoded>
			<wfw:commentRss>http://markbennett.ca/2009/04/updated-bringing-tdd-to-drupal-5/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHOTO FRIDAY: Extreme Closeup</title>
		<link>http://markbennett.ca/2009/03/photo-friday-extreme-closeup/</link>
		<comments>http://markbennett.ca/2009/03/photo-friday-extreme-closeup/#comments</comments>
		<pubDate>Fri, 27 Mar 2009 09:05:13 +0000</pubDate>
		<dc:creator>Mark</dc:creator>
				<category><![CDATA[Photo Friday]]></category>
		<category><![CDATA[Photography]]></category>

		<guid isPermaLink="false">http://markbennett.ca/?p=185</guid>
		<description><![CDATA[
The theme for this weeks PhotoFriday is, &#8216;Extreme Close-up&#8216;.  This is as close as I would like to get to this fellow.
]]></description>
			<content:encoded><![CDATA[<p><img class="pie-img" src="http://lh3.ggpht.com/_wXgxRnf2n70/ScyU4bflHWI/AAAAAAAAKJA/zHhI4wRsk0Q/_DSC0179.jpg?imgmax=720" alt="_DSC0179.jpg" width="720" height="482" /></p>
<p>The theme for this weeks <a href="http://www.photofriday.com">PhotoFriday</a> is, &#8216;<a href="http://www.photofriday.com/archives/challenge/000862.php">Extreme Close-up</a>&#8216;.  This is as close as I would like to get to this fellow.</p>
]]></content:encoded>
			<wfw:commentRss>http://markbennett.ca/2009/03/photo-friday-extreme-closeup/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Bringing test driven development to Drupal 5</title>
		<link>http://markbennett.ca/2009/03/bringing-tdd-to-drupal-5/</link>
		<comments>http://markbennett.ca/2009/03/bringing-tdd-to-drupal-5/#comments</comments>
		<pubDate>Fri, 27 Mar 2009 08:27:44 +0000</pubDate>
		<dc:creator>Mark</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[Spotlight]]></category>
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://markbennett.ca/?p=141</guid>
		<description><![CDATA[UPDATE: Since this post was written phpunit_setup.inc has been moved into the Drupal TDD project. phpunit_setup.inc has also been updated to not require the drupal6_backports.inc file.  Read my post on the changes for the details.
Inspired by the work of Pat Shaughnessy in a series of great articles he wrote on Test Driven Development (TDD) using Drupal (1, 2, 3, 4, 5, 6, 7, [...]]]></description>
			<content:encoded><![CDATA[<p><strong>UPDATE:</strong> Since this post was written phpunit_setup.inc has been moved into the <a href="http://github.com/patshaughnessy/drupal_tdd/tree/master">Drupal TDD</a> project. phpunit_setup.inc has also been updated to not require the drupal6_backports.inc file.  <a href="http://markbennett.ca/2009/04/updated-bringing-tdd-to-drupal-5/">Read my post on the changes for the details.</a></p>
<p>Inspired by the work of <a href="http://patshaughnessy.net/">Pat Shaughnessy</a> in a series of great articles he wrote on <a href="http://en.wikipedia.org/wiki/Test-driven_development">Test Driven Development (TDD)</a> using <a href="http://drupal.org/">Drupal</a> (<a href="http://patshaughnessy.net/2008/9/3/drupal-tdd-1">1</a>, <a href="http://patshaughnessy.net/2008/12/9/example-drupal-module-to-use-for-tdd-demonstration">2</a>, <a href="http://patshaughnessy.net/2008/12/12/writing-your-first-phpunit-test-in-drupal">3</a>, <a href="http://patshaughnessy.net/2008/12/19/using-tdd-to-write-a-drupal-module">4</a>, <a href="http://patshaughnessy.net/2009/1/9/tdd-keeps-your-php-code-separate-from-drupal">5</a>, <a href="http://patshaughnessy.net/2009/1/16/using-a-test-database-with-drupal-unit-tests">6</a>, <a href="http://patshaughnessy.net/2009/1/19/using-mysql-transactions-with-drupal-unit-tests">7</a>, and <a href="http://patshaughnessy.net/2009/1/19/drupal-test-database">8</a>) I was motivated to take his code and use it on my current Drupal project.</p>
<p><span id="more-141"></span></p>
<h3>Pat&#8217;s Solution</h3>
<p>Following the pattern of TDD established by <a href="http://rubyonrails.org">Ruby on Rails</a>, Pat introduced the use of a command-line unit testing framework (<a href="http://www.phpunit.de/">PHPUnit</a>) and then works the reader through using it to develop a simple Drupal module.  Inevitably Pat reaches a point in his example where he must begin loading test data into the database if his code is to be tested properly.</p>
<p>He begins by experimenting with different techingues to stage and unstage data at the beginning of each unit test.  Perhaps inevitably he then run into problems when tests fail or interact with each other in unanticipated ways.  This is where he takes it to the next level.</p>
<p>First, he introduces a system to initialize a seperate test database at the start of each testing cycle.  This ensures that the test database is unaffected by the production data and can&#8217;t accidentally corrupt a production website.</p>
<p>Second, he runs each unit test in it&#8217;s own database transaction.  Transactions are a database tool which allow each database connection to work with it&#8217;s own unique snapshot of the database.  Any changes made to the database only appear on that connection until the transaction is committed to the database.  If the connection is closed the changes are never committed, and the connection has no permanent affect on the database.</p>
<p>Practically speaking transactions allow each test to load any data they require and make any changes to the database they need without needing to worry about cleaning up after them selves.  When the test is over the database rolls back the tests changes automatically and efficiently leaving a clean slate for the next unit test.</p>
<p>Pat wraps all of these changes up in a file called <a href="http://patshaughnessy.net/code/drupal-tdd-4/phpunit_setup.inc">phpunit_setup.inc</a> which you just just have to include at the start of your tests.  It expects that you&#8217;ve created a test database and a user who can access it, and entered the connection details into the list of database connection in Drupal.  (See his original post for <a href="http://patshaughnessy.net/2009/1/19/drupal-test-database">the details</a>.)</p>
<p>Not only is this solution easier to maintain than complex database snapshots and loading scripts, but it&#8217;s more performant.</p>
<h3>Implementing TDD in Drupal 5</h3>
<p>Now let&#8217;s bring this great code of Pat&#8217;s back into Drupal 5.  Let&#8217;s try running it like we would in Drupal 6 and see what happens:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$ phpunit TestDataExampleTest2 modules<span style="color: #000000; font-weight: bold;">/</span>tdd<span style="color: #000000; font-weight: bold;">/</span>TddTests.php
Fatal error: Call to undefined <span style="color: #000000; font-weight: bold;">function</span> drupal_install_system<span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #7a0874; font-weight: bold;">&#41;</span> <span style="color: #000000; font-weight: bold;">in</span>
<span style="color: #000000; font-weight: bold;">/</span>home<span style="color: #000000; font-weight: bold;">/</span>mark<span style="color: #000000; font-weight: bold;">/</span>Work<span style="color: #000000; font-weight: bold;">/</span>LitDistCo<span style="color: #000000; font-weight: bold;">/</span>litdistco-website<span style="color: #000000; font-weight: bold;">/</span>includes<span style="color: #000000; font-weight: bold;">/</span>phpunit_setup.inc on line <span style="color: #000000;">85</span></pre></div></div>

<p>Hmm, that&#8217;s a problem.  A little digging in the <a href="http://api.drupal.org/api/6">Drupal 6 API documentation</a> reveals that the <a href="http://api.drupal.org/api/function/drupal_install_system/6">drupal_install_system() </a>function is new in Drupal 6 and isn&#8217;t present in Drupal 5.  As we&#8217;re not interested in waiting for the client to upgrade to Drupal 6, we&#8217;re going to backport it ourself!</p>
<p>Since we want to avoid modify Pat&#8217;s code as much as possible we&#8217;re going to create a new include file called <a href="http://markbennett.ca/code/drupal_6_backports.inc">drupal6_backports.inc</a>.  This will contain comparable versions of the Drupal 6 functions, which provide the functionality neccesary to run Pat&#8217;s code.  Any of your Drupal 5 tests will need to include this file before you include <a href="http://patshaughnessy.net/code/drupal-tdd-4/phpunit_setup.inc">phpunit_setup.inc</a>.  The code looks like this:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #666666; font-style: italic;"># Include the functions from Drupal 6 neccesary to run phpunit_setup.inc
</span><span style="color: #666666; font-style: italic;"># REMOVE THIS INCLUDE ONCE YOU'VE UPGRADED TO DRUPAL 6
</span><span style="color: #b1b100;">require_once</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'includes/drupal6_backports.inc'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>Later on when the tests are running on Drupal 6 you can remove these includes.</p>
<p>To backport drupal_install_system() I began by looking at the the source code from the Drupal API documentation:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">function</span> drupal_install_system<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
  <span style="color: #000088;">$system_path</span> <span style="color: #339933;">=</span> <span style="color: #990000;">dirname</span><span style="color: #009900;">&#40;</span>drupal_get_filename<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'module'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'system'</span><span style="color: #339933;">,</span> <span style="color: #009900; font-weight: bold;">NULL</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #b1b100;">require_once</span> <span style="color: #0000ff;">'./'</span><span style="color: #339933;">.</span> <span style="color: #000088;">$system_path</span> <span style="color: #339933;">.</span><span style="color: #0000ff;">'/system.install'</span><span style="color: #339933;">;</span>
  module_invoke<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'system'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'install'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #000088;">$system_versions</span> <span style="color: #339933;">=</span> drupal_get_schema_versions<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'system'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #000088;">$system_version</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$system_versions</span> ? <span style="color: #990000;">max</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$system_versions</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">:</span> SCHEMA_INSTALLED<span style="color: #339933;">;</span>
  db_query<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;INSERT INTO {system} (filename, name, type, owner, status, throttle, bootstrap, schema_version) VALUES('<span style="color: #009933; font-weight: bold;">%s</span>', '<span style="color: #009933; font-weight: bold;">%s</span>', '<span style="color: #009933; font-weight: bold;">%s</span>', '<span style="color: #009933; font-weight: bold;">%s</span>', <span style="color: #009933; font-weight: bold;">%d</span>, <span style="color: #009933; font-weight: bold;">%d</span>, <span style="color: #009933; font-weight: bold;">%d</span>, <span style="color: #009933; font-weight: bold;">%d</span>)&quot;</span><span style="color: #339933;">,</span> <span style="color: #000088;">$system_path</span> <span style="color: #339933;">.</span><span style="color: #0000ff;">'/system.module'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'system'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'module'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">''</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">1</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">,</span> <span style="color: #000088;">$system_version</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #666666; font-style: italic;">// Now that we've installed things properly, bootstrap the full Drupal environment</span>
  drupal_bootstrap<span style="color: #009900;">&#40;</span>DRUPAL_BOOTSTRAP_FULL<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  module_rebuild_cache<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>There don&#8217;t appear to be any new Drupal 6 functions in this code so I pasted it into <a href="http://markbennett.ca/code/drupal_6_backports.inc">drupal6_backports.inc</a> as is and ran the unit tests again (remembering to add the line to include the backports mentioned above.)</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$ phpunit TestDataExampleTest2 modules<span style="color: #000000; font-weight: bold;">/</span>tdd<span style="color: #000000; font-weight: bold;">/</span>TddTests.php
Fatal error: Call to undefined <span style="color: #000000; font-weight: bold;">function</span> default_profile_tasks<span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #7a0874; font-weight: bold;">&#41;</span> <span style="color: #000000; font-weight: bold;">in</span>
<span style="color: #000000; font-weight: bold;">/</span>home<span style="color: #000000; font-weight: bold;">/</span>mark<span style="color: #000000; font-weight: bold;">/</span>Work<span style="color: #000000; font-weight: bold;">/</span>LitDistCo<span style="color: #000000; font-weight: bold;">/</span>litdistco-website<span style="color: #000000; font-weight: bold;">/</span>includes<span style="color: #000000; font-weight: bold;">/</span>phpunit_setup.inc on line <span style="color: #000000;">88</span></pre></div></div>

<p>Closer but it looks like we&#8217;re still missing something.  This time it&#8217;s the default_profile_tasks() function.  The  code from the Drupal 6 API documentation looks like this:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">function</span> default_profile_tasks<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>amp<span style="color: #339933;">;</span><span style="color: #000088;">$task</span><span style="color: #339933;">,</span> <span style="color: #000088;">$url</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
  <span style="color: #666666; font-style: italic;">// Insert default user-defined node types into the database. For a complete</span>
  <span style="color: #666666; font-style: italic;">// list of available node type attributes, refer to the node type API</span>
  <span style="color: #666666; font-style: italic;">// documentation at: http://api.drupal.org/api/HEAD/function/hook_node_info.</span>
  <span style="color: #000088;">$types</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
    <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
      <span style="color: #0000ff;">'type'</span> <span style="color: #339933;">=&amp;</span>gt<span style="color: #339933;">;</span> <span style="color: #0000ff;">'page'</span><span style="color: #339933;">,</span>
      <span style="color: #0000ff;">'name'</span> <span style="color: #339933;">=&amp;</span>gt<span style="color: #339933;">;</span> st<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Page'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>
      <span style="color: #0000ff;">'module'</span> <span style="color: #339933;">=&amp;</span>gt<span style="color: #339933;">;</span> <span style="color: #0000ff;">'node'</span><span style="color: #339933;">,</span>
      <span style="color: #0000ff;">'description'</span> <span style="color: #339933;">=&amp;</span>gt<span style="color: #339933;">;</span> st<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;A &amp;lt;em&amp;gt;page&amp;lt;/em&amp;gt;, similar in form to a &amp;lt;em&amp;gt;story&amp;lt;/em&amp;gt;, is a simple method for creating and displaying information that rarely changes, such as an <span style="color: #000099; font-weight: bold;">\&quot;</span>About us<span style="color: #000099; font-weight: bold;">\&quot;</span> section of a website. By default, a &amp;lt;em&amp;gt;page&amp;lt;/em&amp;gt; entry does not allow visitor comments and is not featured on the site's initial home page.&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>
      <span style="color: #0000ff;">'custom'</span> <span style="color: #339933;">=&amp;</span>gt<span style="color: #339933;">;</span> <span style="color: #009900; font-weight: bold;">TRUE</span><span style="color: #339933;">,</span>
      <span style="color: #0000ff;">'modified'</span> <span style="color: #339933;">=&amp;</span>gt<span style="color: #339933;">;</span> <span style="color: #009900; font-weight: bold;">TRUE</span><span style="color: #339933;">,</span>
      <span style="color: #0000ff;">'locked'</span> <span style="color: #339933;">=&amp;</span>gt<span style="color: #339933;">;</span> <span style="color: #009900; font-weight: bold;">FALSE</span><span style="color: #339933;">,</span>
      <span style="color: #0000ff;">'help'</span> <span style="color: #339933;">=&amp;</span>gt<span style="color: #339933;">;</span> <span style="color: #0000ff;">''</span><span style="color: #339933;">,</span>
      <span style="color: #0000ff;">'min_word_count'</span> <span style="color: #339933;">=&amp;</span>gt<span style="color: #339933;">;</span> <span style="color: #0000ff;">''</span><span style="color: #339933;">,</span>
    <span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>
    <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
      <span style="color: #0000ff;">'type'</span> <span style="color: #339933;">=&amp;</span>gt<span style="color: #339933;">;</span> <span style="color: #0000ff;">'story'</span><span style="color: #339933;">,</span>
      <span style="color: #0000ff;">'name'</span> <span style="color: #339933;">=&amp;</span>gt<span style="color: #339933;">;</span> st<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Story'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>
      <span style="color: #0000ff;">'module'</span> <span style="color: #339933;">=&amp;</span>gt<span style="color: #339933;">;</span> <span style="color: #0000ff;">'node'</span><span style="color: #339933;">,</span>
      <span style="color: #0000ff;">'description'</span> <span style="color: #339933;">=&amp;</span>gt<span style="color: #339933;">;</span> st<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;A &amp;lt;em&amp;gt;story&amp;lt;/em&amp;gt;, similar in form to a &amp;lt;em&amp;gt;page&amp;lt;/em&amp;gt;, is ideal for creating and displaying content that informs or engages website visitors. Press releases, site announcements, and informal blog-like entries may all be created with a &amp;lt;em&amp;gt;story&amp;lt;/em&amp;gt; entry. By default, a &amp;lt;em&amp;gt;story&amp;lt;/em&amp;gt; entry is automatically featured on the site's initial home page, and provides the ability to post comments.&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>
      <span style="color: #0000ff;">'custom'</span> <span style="color: #339933;">=&amp;</span>gt<span style="color: #339933;">;</span> <span style="color: #009900; font-weight: bold;">TRUE</span><span style="color: #339933;">,</span>
      <span style="color: #0000ff;">'modified'</span> <span style="color: #339933;">=&amp;</span>gt<span style="color: #339933;">;</span> <span style="color: #009900; font-weight: bold;">TRUE</span><span style="color: #339933;">,</span>
      <span style="color: #0000ff;">'locked'</span> <span style="color: #339933;">=&amp;</span>gt<span style="color: #339933;">;</span> <span style="color: #009900; font-weight: bold;">FALSE</span><span style="color: #339933;">,</span>
      <span style="color: #0000ff;">'help'</span> <span style="color: #339933;">=&amp;</span>gt<span style="color: #339933;">;</span> <span style="color: #0000ff;">''</span><span style="color: #339933;">,</span>
      <span style="color: #0000ff;">'min_word_count'</span> <span style="color: #339933;">=&amp;</span>gt<span style="color: #339933;">;</span> <span style="color: #0000ff;">''</span><span style="color: #339933;">,</span>
    <span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>
  <span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
  <span style="color: #b1b100;">foreach</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$types</span> <span style="color: #b1b100;">as</span> <span style="color: #000088;">$type</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000088;">$type</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>object<span style="color: #009900;">&#41;</span> _node_type_set_defaults<span style="color: #009900;">&#40;</span><span style="color: #000088;">$type</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    node_type_save<span style="color: #009900;">&#40;</span><span style="color: #000088;">$type</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
&nbsp;
  <span style="color: #666666; font-style: italic;">// Default page to not be promoted and have comments disabled.</span>
  variable_set<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'node_options_page'</span><span style="color: #339933;">,</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'status'</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  variable_set<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'comment_page'</span><span style="color: #339933;">,</span> COMMENT_NODE_DISABLED<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
  <span style="color: #666666; font-style: italic;">// Don't display date and author information for page nodes by default.</span>
  <span style="color: #000088;">$theme_settings</span> <span style="color: #339933;">=</span> variable_get<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'theme_settings'</span><span style="color: #339933;">,</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #000088;">$theme_settings</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'toggle_node_info_page'</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #009900; font-weight: bold;">FALSE</span><span style="color: #339933;">;</span>
  variable_set<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'theme_settings'</span><span style="color: #339933;">,</span> <span style="color: #000088;">$theme_settings</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
  <span style="color: #666666; font-style: italic;">// Update the menu router information.</span>
  menu_rebuild<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>We can repeat the same process as last time, taking the code from Drupal 6 and inserting it into <a href="http://markbennett.ca/code/drupal_6_backports.inc">drupal6_backports.inc</a> then running the script again.</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$ phpunit TestDataExampleTest2 modules<span style="color: #000000; font-weight: bold;">/</span>tdd<span style="color: #000000; font-weight: bold;">/</span>TddTests.php
&nbsp;
Fatal error: Call to undefined <span style="color: #000000; font-weight: bold;">function</span> actions_synchronize<span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #7a0874; font-weight: bold;">&#41;</span> <span style="color: #000000; font-weight: bold;">in</span>
<span style="color: #000000; font-weight: bold;">/</span>home<span style="color: #000000; font-weight: bold;">/</span>mark<span style="color: #000000; font-weight: bold;">/</span>Work<span style="color: #000000; font-weight: bold;">/</span>LitDistCo<span style="color: #000000; font-weight: bold;">/</span>litdistco-website<span style="color: #000000; font-weight: bold;">/</span>includes<span style="color: #000000; font-weight: bold;">/</span>phpunit_setup.inc on line <span style="color: #000000;">90</span></pre></div></div>

<p>I actually had to repeat this process twice more.  Once to add the actions_synchronize() function and again to add the _drupal_flush_css_js() function.  Each of these functions don&#8217;t really make sense in the context of Drupal 5 since one invokes actions only associated with Drupal 6 modules, and the other refreshes the random strings addes to CSS and javascript files which also doesn&#8217;t happen in Drupal 5.  This makes the code for both of them very easy to write:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">function</span> actions_synchronize<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
  <span style="color: #666666; font-style: italic;"># Just included for compatibility with newer Drupal version
</span>  <span style="color: #666666; font-style: italic;"># Don't do anything
</span>  <span style="color: #b1b100;">return</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #000000; font-weight: bold;">function</span>  _drupal_flush_css_js<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
  <span style="color: #666666; font-style: italic;"># Just included for compatability with newer Drupal version
</span>  <span style="color: #666666; font-style: italic;"># Don't do anything
</span>  <span style="color: #b1b100;">return</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>By including <a href="http://markbennett.ca/code/drupal_6_backports.inc">drupal_6_backports.inc</a> before <a href="http://patshaughnessy.net/code/drupal-tdd-4/phpunit_setup.inc">phpunit_setup.inc</a> at the top of the tests you can now execute them and they will begin to run. And run.  AND RUN!!!</p>
<p>What&#8217;s going on here?</p>
<p>Stepping into the <a href="http://patshaughnessy.net/code/drupal-tdd-4/phpunit_setup.inc">phpunit_setup.inc</a> script I can see the test database connection being opened.  It then calls this function:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">function</span> each_table<span style="color: #009900;">&#40;</span><span style="color: #000088;">$table_callback</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
  <span style="color: #000000; font-weight: bold;">global</span> <span style="color: #000088;">$db_url</span><span style="color: #339933;">;</span>
  <span style="color: #000088;">$url</span> <span style="color: #339933;">=</span> <span style="color: #990000;">parse_url</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$db_url</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'test'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #000088;">$database</span> <span style="color: #339933;">=</span> <span style="color: #990000;">substr</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$url</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'path'</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #000088;">$result</span> <span style="color: #339933;">=</span> db_query<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;SELECT table_name FROM information_schema.tables WHERE table_schema = '<span style="color: #006699; font-weight: bold;">$database</span>'&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #b1b100;">while</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$table</span> <span style="color: #339933;">=</span> db_result<span style="color: #009900;">&#40;</span><span style="color: #000088;">$result</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000088;">$table_callback</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$table</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>This function call the $table_callback() function once for each table in the database, passing the table name in as a parameter.  If I inspect the value of $table however, I see that it&#8217;s simply passing the same $table name again and again.  Worse the expression:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000088;">$table</span> <span style="color: #339933;">=</span> db_result<span style="color: #009900;">&#40;</span><span style="color: #000088;">$result</span><span style="color: #009900;">&#41;</span></pre></div></div>

<p>never evaulates to false, so the code never exits.  So why is this happening?</p>
<p>It turns out the Drupal 6 and 7 redefine the db_result() function such that subsequent calls will step through the results of a query one row at a time.  In Drupal 5 db_result() takes a second optional parameter indicating which row of the results to read.  It&#8217;s defined like this:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">function</span> db_result<span style="color: #009900;">&#40;</span><span style="color: #000088;">$result</span><span style="color: #339933;">,</span> <span style="color: #000088;">$row</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">0</span><span style="color: #009900;">&#41;</span></pre></div></div>

<p>So we&#8217;re not actually iterating through the results as expected but reading the first row of the result set again and again.  As a compromise I&#8217;ve changed Pat&#8217;s original function to:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">function</span> each_table<span style="color: #009900;">&#40;</span><span style="color: #000088;">$table_callback</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
  <span style="color: #000000; font-weight: bold;">global</span> <span style="color: #000088;">$db_url</span><span style="color: #339933;">;</span>
  <span style="color: #000088;">$url</span> <span style="color: #339933;">=</span> <span style="color: #990000;">parse_url</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$db_url</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'test'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #000088;">$database</span> <span style="color: #339933;">=</span> <span style="color: #990000;">substr</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$url</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'path'</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #000088;">$result</span> <span style="color: #339933;">=</span> db_query<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;SELECT table_name FROM information_schema.tables WHERE table_schema = '<span style="color: #006699; font-weight: bold;">$database</span>'&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #b1b100;">while</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$table</span> <span style="color: #339933;">=</span> db_fetch_array<span style="color: #009900;">&#40;</span><span style="color: #000088;">$result</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000088;">$table_callback</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$table</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'table_name'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>I&#8217;ve replaced the call to db_result() with one to db_fetch_array().  db_fetch_array() works the same in Drupal 6 as it does in Drupal 5 so this code will work fine in both versions.</p>
<p>Running it again we see that the tests run correctly:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$ phpunit TestDataExampleTest2 modules<span style="color: #000000; font-weight: bold;">/</span>tdd<span style="color: #000000; font-weight: bold;">/</span>TddTests.php
PHPUnit 3.2.16 by Sebastian Bergmann.
&nbsp;
..
&nbsp;
Time: <span style="color: #000000;">0</span> seconds
&nbsp;
OK <span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #000000;">2</span> tests<span style="color: #7a0874; font-weight: bold;">&#41;</span></pre></div></div>

<p>My patch has been submitted back to Pat so that he&#8217;ll hopefully integrate them back into his post however for the time being I&#8217;ve packaged my changes into <a href="http://markbennett.ca/code/phpunit_setup_drupal5.inc">phpunit_setup_drupal5.inc</a>.</p>
<p>And that&#8217;s it!  By following Pat&#8217;s instructions and including <a href="http://markbennett.ca/code/drupal_6_backports.inc">drupal_6_backports.inc</a> and <a href="http://markbennett.ca/code/phpunit_setup_drupal5.inc">phpunit_setup_drupal5.inc</a> in your tests you can perform full test driven development with test fixtures and an isolated test database in Drupal 5!  Now go try it for yourself!</p>
<p><strong>UPDATE:</strong> I should point out that if you&#8217;re going to test your own modules, you need to edit the profiles/default/default.profile and include your modules in the default_profile_modules() function on line 11.</p>
]]></content:encoded>
			<wfw:commentRss>http://markbennett.ca/2009/03/bringing-tdd-to-drupal-5/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
