<?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/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Roy's Weblog</title>
	<atom:link href="http://royw.wordpress.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://royw.wordpress.com</link>
	<description>Just another WordPress.com weblog</description>
	<lastBuildDate>Mon, 19 Jul 2010 06:07:32 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='royw.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://s2.wp.com/i/buttonw-com.png</url>
		<title>Roy's Weblog</title>
		<link>http://royw.wordpress.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://royw.wordpress.com/osd.xml" title="Roy&#039;s Weblog" />
	<atom:link rel='hub' href='http://royw.wordpress.com/?pushpress=hub'/>
		<item>
		<title>My Media Center v3.0 &#8211; Day 2</title>
		<link>http://royw.wordpress.com/2010/07/19/my-media-center-v3-0-day-2/</link>
		<comments>http://royw.wordpress.com/2010/07/19/my-media-center-v3-0-day-2/#comments</comments>
		<pubDate>Mon, 19 Jul 2010 06:07:13 +0000</pubDate>
		<dc:creator>royw</dc:creator>
				<category><![CDATA[Media Center]]></category>

		<guid isPermaLink="false">http://royw.wordpress.com/?p=140</guid>
		<description><![CDATA[I didn&#8217;t spend as much time today as I had hoped, but did make some progress. The Plex &#8220;App Store&#8221; which while it isn&#8217;t really a store, is still pretty slick. Tried several plug-ins, but will just hit the highlights here. Couldn&#8217;t get Hulu plugin to work at all &#8211; probably wrong credentials. The pandora [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=royw.wordpress.com&amp;blog=4979120&amp;post=140&amp;subd=royw&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>I didn&#8217;t spend as much time today as I had hoped, but did make some progress.</p>
<p>The Plex &#8220;App Store&#8221; which while it isn&#8217;t really a store, is still pretty slick.  Tried several plug-ins, but will just hit the highlights here.  Couldn&#8217;t get Hulu plugin to work at all &#8211; probably wrong credentials.  The pandora plug-in did work but feels hackish as the display is a cut section from safari viewing pandora.  I ended up watch &#8220;Living With Father (1947)&#8221; while testing the iMovie plug-in.  I noticed one burp that might have been network streaming and one cut section that is probably in the source material.  The video quality was not the best, but most was probably due to the source.  The National Geographic plug-in was a little disappointing in that the info boxes disappear before I could completely read them.  Also not a lot of photos.  I installed several others but haven&#8217;t given them a try yet.</p>
<p>I did put new batteries in my Gyration keyboard and that works well with the exception that the media keys don&#8217;t do anything and I haven&#8217;t found how to configure them yet.</p>
<p>Watched an iso movie from the NAS and the player was perfect.  OK, it was a drama (Fat Man &amp; Little Boy) so was not a stress test.</p>
<p>Still trying to move the media content from the old XBMC box to the NAS.  I bumped a switch which lost power and interrupted the first copy.  I then tried using cp -ur but that started copying the same files over.  Ended up writing a ruby script to finish the sync with and that is still in process.</p>
<p>So the network reconfiguration is postponed until next weekend.  But I did find that MacPorts has dnsmasq.  So installed XCode so I could install MacPorts.  I&#8217;ve installed dnsmasq but have not yet configured or started it.</p>
<p>Did some research on skins as I really miss being able to view the content by genre.  There are a few skins that I need to give a try (Aeon, Alaska), but the really good news is there is a confluence skin in the works.</p>
<p>What&#8217;s really sweet is that after a day and a half, I&#8217;m feeling comfortable leaving the new system for the family to use for the week.</p>
<p>Have a great week!</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/royw.wordpress.com/140/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/royw.wordpress.com/140/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/royw.wordpress.com/140/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/royw.wordpress.com/140/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/royw.wordpress.com/140/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/royw.wordpress.com/140/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/royw.wordpress.com/140/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/royw.wordpress.com/140/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/royw.wordpress.com/140/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/royw.wordpress.com/140/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/royw.wordpress.com/140/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/royw.wordpress.com/140/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/royw.wordpress.com/140/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/royw.wordpress.com/140/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=royw.wordpress.com&amp;blog=4979120&amp;post=140&amp;subd=royw&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://royw.wordpress.com/2010/07/19/my-media-center-v3-0-day-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/07aaa2c67e8b41fa38f628356a2b163a?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">royw</media:title>
		</media:content>
	</item>
		<item>
		<title>My Media Center v3.0 &#8211; Day 1</title>
		<link>http://royw.wordpress.com/2010/07/18/my-media-center-v3-0-day-1/</link>
		<comments>http://royw.wordpress.com/2010/07/18/my-media-center-v3-0-day-1/#comments</comments>
		<pubDate>Sun, 18 Jul 2010 07:39:31 +0000</pubDate>
		<dc:creator>royw</dc:creator>
				<category><![CDATA[Media Center]]></category>

		<guid isPermaLink="false">http://royw.wordpress.com/?p=134</guid>
		<description><![CDATA[Day one of replacing a gentoo hosted XBMC system with a mac mini hosted Plex system.<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=royw.wordpress.com&amp;blog=4979120&amp;post=134&amp;subd=royw&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>OK, I&#8217;ll admit it.  I like to watch movies.</p>
<p>With a DVD library of over 1200 titles, I&#8217;ve been on a multi-year quest for the perfect media center system.  Today I took the next major step on that journey.</p>
<p>Here&#8217;s a brief recap of my journey to date.  For several years I&#8217;d toyed with the idea of a media center, then the release of LinuxMCE caused me to take the plunge.  Built up a system.  Hit a few hardware issues, rebuilt it with a new motherboard.  Distributed the media on three systems via NFS.  But was never really happy with LinuxMCE, probably because I just wanted a media center and not a home control center.  Just too complex and persistent player problems drove me to try XBMC.  I&#8217;ll admit it, I just loved XBMC, it&#8217;s way cool.  I wanted a plug in replacement for LinuxMCE which meant the system would be a router with firewall and DHCP/DNS server for my media LAN, so went with a gentoo system, which, IMO, is easier to setup and configure than a ubuntu system.  But with no time to maintain the gentoo hosted system, and my growing comfort with Macbook laptops, I just decided to jump ship to a Mac Mini running Plex (a fork of XBMC for the mac).</p>
<p>So day one.  </p>
<p>Purchased a 2.4GHz, 4GB, 2010 mac mini (with HDMI port).  Also went by Radio Shack to get a HDMI cable and was surprised at how proud they are of their cables &#8211; ouch.  </p>
<p>Initial setup using one of my LG246 HDMI monitors instead of the plasma TV.  Mini boots right up, real simple snow leopard config.  First issue is the display needed some underscan adjusting under Settings, Displays.  Then update the system to the latest and it&#8217;s time to have fun. </p>
<p>Download Plex and install.  Install the recommended Candelair driver for Apple Remote support.  Candelair recommended/advertised Remote Buddy which looked pretty cool so installed the 30 day trial.</p>
<p>Also installed iTerm and Firefox.</p>
<p>My current network has four media sources:  a kubuntu workstation with 3TB for media, the old XBMC system with 1TB, a gentoo workstation with 5TB, and a new Thecus N4100Pro NAS Raid5 with 4 2TB drives.  I&#8217;ve moved my m4v files to the Thecus and started the copying of the iso files from the XBMC system to the Thecus (currently still in progress) in prep for decommissioning the XBMC system.  I intend to eventually transcode all the iso images to m4v and have the Thecus as the primary media server.</p>
<p>So for now, just mounted the NFS volumes from the kubuntu and Thecus systems on the mac mini using Disk Utility.  Here&#8217;s the first difference between XBMC linux and Plex mac &#8211; on the XBMC when adding the source, I normally just specify the path to the autofs mount point.  But on the mac Plex, I needed to just select the mounted volume as a source.  The first gotcha is that the source must be mounted.  Currently I&#8217;m doing that with Finder, but need to find the mac way of making sure it is mounted on boot.</p>
<p>I had a little frustration with Plex trying to use the default keyboard only.  Eventually enabled the mouse primarily so I could right click and remove a bad source definition.  For now, I&#8217;m planning on leaving the mouse enabled.</p>
<p>Activated the Remote Buddy and configured it to start at login.  Set the AJAX password.  Then brought up a safari on my iphone and pointed it at the mac mini.  Very nice.  It did take me a while to figure out that the &#8220;Menu&#8221; button is functionally equivalent to the ESC key.  One cool hint, use the &#8216;+&#8217; button in safari to add a link to your home screen.</p>
<p>Went to Plex&#8217;s app store and added a few neat sounding extensions.  Primarily the HDHomeRun app, which is pretty cool.  It does take a while to verify each TV channel, so did just a few for now.</p>
<p>Moved the system to the home theater and connected the HDMI to my Yamaha RX-V861 A/V receiver.  The video was perfect but no sound.  Changed Plex&#8217;s system audio preference from built-in output to HDMI and that was solved.</p>
<p>Also hooked up Gyration air mouse to the mac mini.</p>
<p>Finally added Plex to the login.</p>
<p>So ended the first day with a functional mac mini running Plex by default which accesses media from two different NAS systems controlled by my iPhone, iPad and airmouse, sending video and audio over HDMI to my home theater.  </p>
<p>TODO list includes reconfiguring my media LAN to remove the XBMC machine.  This will necessitate setting up and running a replacement DHCP/DNS server (hopefully either dnsmasq or something equivalent).  Also will need to change the mode of my wireless access point to be a gateway to my full home LAN.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/royw.wordpress.com/134/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/royw.wordpress.com/134/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/royw.wordpress.com/134/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/royw.wordpress.com/134/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/royw.wordpress.com/134/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/royw.wordpress.com/134/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/royw.wordpress.com/134/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/royw.wordpress.com/134/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/royw.wordpress.com/134/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/royw.wordpress.com/134/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/royw.wordpress.com/134/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/royw.wordpress.com/134/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/royw.wordpress.com/134/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/royw.wordpress.com/134/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=royw.wordpress.com&amp;blog=4979120&amp;post=134&amp;subd=royw&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://royw.wordpress.com/2010/07/18/my-media-center-v3-0-day-1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/07aaa2c67e8b41fa38f628356a2b163a?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">royw</media:title>
		</media:content>
	</item>
		<item>
		<title>Ruby Gem Recipe</title>
		<link>http://royw.wordpress.com/2010/06/07/ruby-gem-recipe/</link>
		<comments>http://royw.wordpress.com/2010/06/07/ruby-gem-recipe/#comments</comments>
		<pubDate>Mon, 07 Jun 2010 06:30:02 +0000</pubDate>
		<dc:creator>royw</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[gem]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://royw.wordpress.com/?p=110</guid>
		<description><![CDATA[One of the first hurdles new ruby programmers run into is creating a project as a gem. Today there are some great tools that trivialize gem creation and publication: jeweler, bundler, github, gemcutter. In this article I&#8217;ll show the creation of an example application with a command line interface all packaged in a gem. Example [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=royw.wordpress.com&amp;blog=4979120&amp;post=110&amp;subd=royw&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>One of the first hurdles new ruby programmers run into is creating a project as a gem.  Today there are some great tools that trivialize gem creation and publication: jeweler, bundler, github, gemcutter.  In this article I&#8217;ll show the creation of an example application with a command line interface all packaged in a gem.</p>
<p><span id="more-110"></span></p>
<p>Example code available at:  http://github.com/royw/creating-gem-example</p>
<pre class="brush: plain; gutter: false;">
 ∴ git clone git://github.com/royw/creating-gem-example.git
</pre>
<p>First, I really like <a href="http://rvm.beginrescueend.com/">Ruby Version Manager</a> so I highly recommend installing it and the versions of ruby you prefer.  Keeping it simple for this article:</p>
<pre class="brush: plain; gutter: false;">
 ∴ rvm install 1.8.7
 ∴ rvm 1.8.7
</pre>
<p>Second, install the prerequisites <a href="http://github.com/technicalpickles/jeweler">Jeweler</a> and <a href="http://gembundler.com/">Bundler</a> :</p>
<pre class="brush: plain; gutter: false;">
 ∴ gem install jeweler
 ∴ gem install bundler
</pre>
<p>Now it&#8217;s time to code generate the project.  Let&#8217;s see what jeweler can do for us:</p>
<pre class="brush: plain; gutter: false;">
 ∴ jeweler --help
Usage: jeweler [options] reponame
e.g. jeweler the-perfect-gem
        --directory [DIRECTORY]      specify the directory to generate into

        --rspec                      generate rspec code examples
        --shoulda                    generate shoulda tests
        --testunit                   generate test/unit tests
        --bacon                      generate bacon specifications
        --testspec                   generate test/spec tests
        --minitest                   generate minitest tests
        --micronaut                  generate micronaut examples
        --riot                       generate riot tests

        --cucumber                   generate cucumber stories in addition to the other tests

        --reek                       generate rake task for reek
        --roodi                      generate rake task for roodi

        --[no-]gemcutter             setup project for gemcutter
        --rubyforge                  setup project for rubyforge
        --summary [SUMMARY]          specify the summary of the project
        --description [DESCRIPTION]  specify a description of the project

        --user-name [USER_NAME]      the user's name, ie that is credited in the LICENSE
        --user-email [USER_EMAIL]    the user's email, ie that is credited in the Gem specification

        --github-username [GITHUB_USERNAME]
                                     name of the user on GitHub to set the project up under
        --github-token [GITHUB_TOKEN]
                                     GitHub token to use for interacting with the GitHub API
        --git-remote [GIT_REMOTE]    URI to set the git origin remote to
        --homepage [HOMEPAGE]        the homepage for your project (defaults to the GitHub repo)
        --create-repo                create the repository on GitHub

        --yard                       use yard for documentation
        --rdoc                       use rdoc for documentation
    -h, --help                       display this help and exit
</pre>
<p>Wow!  Pretty impressive.  As you can see, jeweler let&#8217;s you define your project your way.  For this article:</p>
<pre class="brush: plain; gutter: false;">
∴ jeweler --rspec --no-gemcutter --summary &quot;example gem for blog article&quot; --description &quot;This is an example gem for my blog article showing how to create a gem&quot; --rdoc --user-name &quot;Roy Wright&quot; --user-email &quot;roy@wright.org&quot; example
        create  .gitignore
        create  Rakefile
        create  LICENSE
        create  README.rdoc
        create  .document
        create  lib
        create  lib/example.rb
        create  spec
        create  spec/spec_helper.rb
        create  spec/example_spec.rb
        create  spec/spec.opts
Jeweler has prepared your gem in example
∴ cd example
∴ ls
LICENSE         README.rdoc     Rakefile        lib             spec
 ∴ rake -T
(in /Users/royw/views/example)
rake build                           # Build gem
rake check_dependencies              # Check that runtime and development dependencies are installed
rake check_dependencies:development  # Check that development dependencies are installed
rake check_dependencies:runtime      # Check that runtime dependencies are installed
rake clobber_rcov                    # Remove rcov products for rcov
rake clobber_rdoc                    # Remove rdoc products
rake gemspec                         # Generate and validates gemspec
rake gemspec:debug                   # Display the gemspec for debugging purposes
rake gemspec:generate                # Generates the gemspec, using version from VERSION
rake gemspec:validate                # Validates the gemspec
rake git:release                     # Tag a release in Git
rake github:release                  # Release Gem to GitHub
rake install                         # Install gem using sudo
rake rcov                            # Run specs using RCov
rake rdoc                            # Build the rdoc HTML Files
rake release                         # Release gem
rake rerdoc                          # Force a rebuild of the RDOC files
rake spec                            # Run specs
rake version                         # Displays the current version
rake version:bump:major              # Bump the gemspec by a major version.
rake version:bump:minor              # Bump the gemspec by a minor version.
rake version:bump:patch              # Bump the gemspec by a patch version.
rake version:write                   # Writes out an explicit version.
</pre>
<p>As you can see, most of the infrastructure is ready to use.  Ah, you caught the &#8220;most&#8221; did you?  OK, let&#8217;s complete it.</p>
<p>First, let&#8217;s get a VERSION file installed:</p>
<pre class="brush: plain; gutter: false;">
 ∴ rake version:write
(in /Users/royw/views/example)
Updated version: 0.0.0
 ∴ ln -s ../VERSION VERSION
</pre>
<p>This puts a copy of the version in our gem so we can have a &#8211;version feature.</p>
<p>If your gem is going to furnish an executable, then do something like:</p>
<pre class="brush: plain; gutter: false;">
 ∴ mkdir bin
 ∴ mate bin/example # add the code below, changing &quot;example&quot; to your project name
 ∴ chmod +x bin/example
 ∴ cat bin/example
</pre>
<pre class="brush: ruby; gutter: true;">
#!/usr/bin/env ruby

require File.expand_path(File.dirname(__FILE__) + &quot;/../lib/example&quot;)
exit CLI.execute
</pre>
<p>Now let&#8217;s switch over to the lib directory and do a little infrastructure improvements.</p>
<pre class="brush: plain; gutter: false;">
 ∴ mkdir lib/example
</pre>
<p>And this directory is where our code goes.  To access our code we modify the lib/example.rb to handle our requires:</p>
<pre class="brush: plain; gutter: false;">
 ∴ cat lib/example.rb
</pre>
<pre class="brush: ruby; gutter: true;">
# not the best solution, probably should use require_relative in ruby 1.9 or the backported
# version from Programming Ruby v3 for ruby 1.8.
$:.unshift(File.dirname(__FILE__)) unless
  $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))

# a few of the gems I use in most projects
require 'rubygems'
require 'extlib'
require 'versionomy'
require 'log4r'
require 'configliere'
require 'singleton'
require 'pp'

# require all of the library files
# note, you may need to specify these explicitly if there are any load order dependencies
Dir['example/**/*.rb'].each do |name|
  require name
end
</pre>
<p>And we will toss the start of a command line interface object (with support for command line arguments, environment variables, config files, and logging) into lib/example/cli.rb:</p>
<pre class="brush: plain; gutter: false;">
 ∴ cat lib/example/cli.rb
</pre>
<pre class="brush: ruby; gutter: true; wrap-lines: false;">
# Command Line Interface to the Example application
#
# Usage:
#
#  exit CLI.execute
#
# Note, this is a Singleton class, only use CLI.execute to access this class.
#
class CLI
  include Singleton

  # The application can monitor this attribute to know if an interrupt (^c) has been signalled.
  attr_reader :interrupt_signalled

  APP_NAME = 'example'

  # The main entry point for the CLI
  # returns the exit code
  def self.execute
    instance.execute
  end

  # protected by the Singleton.  I.e., do not attempt CLI.new
  def initialize
    @interrupt_signalled = false
  end

  # execute the app from the CLI
  # returns the exit code
  def execute
    exit_code = 0
    # ^c handler
    Signal.trap(&quot;INT&quot;) { @interrupt_signalled = true }
    begin
      setup_settings
      Settings[:app_name] = APP_NAME
      if Settings[:version]
        puts Example.version.to_s
      else
        Settings[:logger] = init_logger
        app = Example.new
        app.execute
      end
    rescue SystemExit
      # exit() calls from within the block actually raise SystemExit
    rescue Exception =&amp;gt; e
      Settings[:logger].error e.to_s if Settings[:logger]
      exit_code = 1
    end
    exit_code
  end

  # log4r setup
  def init_logger
    # Initial setup of logger
    logger = Log4r::Logger.new(APP_NAME)
    level_map = {'DEBUG' =&amp;gt; Log4r::DEBUG, 'INFO' =&amp;gt; Log4r::INFO, 'WARN' =&amp;gt; Log4r::WARN}

    # console messages
    logger.outputters = Log4r::StdoutOutputter.new(:console)
    Log4r::Outputter[:console].formatter  = Log4r::PatternFormatter.new(:pattern =&amp;gt; &quot;%m&quot;)
    Log4r::Outputter[:console].level = Log4r::INFO
    Log4r::Outputter[:console].level = Log4r::WARN if Settings[:quiet]
    Log4r::Outputter[:console].level = Log4r::DEBUG if Settings[:debug]
    Log4r::Outputter[:console].formatter = Log4r::PatternFormatter.new(:pattern =&amp;gt; &quot;%m&quot;)
    logger.trace = true if Settings[:trace]

    if Settings[:logfile]
      logfile_outputter = Log4r::RollingFileOutputter.new(:logfile, :filename =&amp;gt; Settings[:logfile], :maxsize =&amp;gt; 1000000 )
      logger.add logfile_outputter
      Settings[:loglevel] ||= 'INFO'
      Log4r::Outputter[:logfile].formatter = Log4r::PatternFormatter.new(:pattern =&amp;gt; &quot;[%l] %d :: %M&quot;)
      Log4r::Outputter[:logfile].level = level_map[Settings[:loglevel].upcase] || Log4r::INFO
    end
    logger
  end

  # Configliere setup
  def setup_settings
    Settings.use :all

    Settings.define :logfile, :type =&amp;gt; String, :description =&amp;gt; 'Log filename, default is: media2nfo.log', :required =&amp;gt; false
    Settings.define :loglevel, :type =&amp;gt; String, :description =&amp;gt; 'Logfile logging level, must be one of: DEBUG, INFO, WARN, ERROR', :required =&amp;gt; false
    Settings.define :debug, :type =&amp;gt; :boolean, :description =&amp;gt; 'Log debug messages to console', :required =&amp;gt; false
    Settings.define :quiet, :type =&amp;gt; :boolean, :description =&amp;gt; 'Only log errors to console', :required =&amp;gt; false
    Settings.define :trace, :type =&amp;gt; :boolean, :description =&amp;gt; 'Trace logging', :required =&amp;gt; false
    Settings.define :version, :type =&amp;gt; :boolean, :description =&amp;gt; 'Application Version', :required =&amp;gt; false

    Settings({
              :logfile =&amp;gt; &quot;#{APP_NAME}.log&quot;,
              :loglevel =&amp;gt; 'debug'
             })

    Settings.read(&quot;#{APP_NAME}.yaml&quot;) if File.exist?(File.expand_path(&quot;~/.configliere/#{APP_NAME}.yaml&quot;))
    Settings.resolve!
  end
end
</pre>
<p>And a really basic application:</p>
<pre class="brush: plain; gutter: false;">
 ∴ cat lib/example/example.rb
</pre>
<pre class="brush: ruby; gutter: true;">
class Example
  def self.version
    ver = Versionomy.parse(IO.read(File.expand_path(File.dirname(__FILE__) + &quot;/../VERSION&quot;)).strip)
    ver
  end
  def execute
    Settings[:logger].debug &quot;Example.execute&quot;
  end
end
</pre>
<p>And to be good, an rspec:</p>
<pre class="brush: plain; gutter: false;">
 ∴ cat spec/example_spec.rb
</pre>
<pre class="brush: ruby; gutter: true;">
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')

describe &quot;Example&quot; do
  it &quot;should return a Versionomy object as the version&quot; do
    ver = Example.version
    ver.instance_of?(Versionomy::Value).should be_true
  end
  it &quot;should return the version stored in the VERSION file&quot; do
    v1 = Example.version.to_s.strip
    v2 = IO.read(File.expand_path(File.dirname(__FILE__) + &quot;/../VERSION&quot;)).strip
    v1.should == v2
  end
end
</pre>
<p>And the last change to the infrastructure is to bundle the gems for two reasons, first to ease deployment, but second, more importantly to resolve dependencies of all gems together instead of individually (which can lead to dependency problems).  So here&#8217;s a real simple Gemfile to get started with:</p>
<pre class="brush: plain; gutter: false;">
 ∴ cat Gemfile
</pre>
<pre class="brush: ruby; gutter: true;">
source :gemcutter

gem 'configliere',  '&amp;gt;=0.0.9'
gem 'extlib',       '&amp;gt;=0.9.15'
gem 'versionomy',   '&amp;gt;=0.4.0'
gem 'log4r',        '&amp;gt;=1.1.7'

gem 'jeweler',      '&amp;gt;=1.4.0'
gem 'rake',         '&amp;gt;=0.8.7'
gem 'rspec',        '&amp;gt;=1.3.0',  :require =&amp;gt; 'spec'
</pre>
<p>Then to install the gems:</p>
<pre class="brush: plain; gutter: false;">
 ∴ bundler install
</pre>
<p><strong>Try it</strong></p>
<pre class="brush: plain; gutter: false;">
 ∴ ls *.log
ls: *.log: No such file or directory
 ∴ bin/example
 ∴ ls *.log
example000001.log
 ∴ cat example000001.log
[DEBUG] 2010-06-07 01:41:12 :: Example.execute
 ∴ bin/example --version
0.1.0
 ∴ bin/example --help
usage: example command [...--param=val...]

Params:
  --debug:                    Log debug messages to console
  --logfile:                  Log filename, default is: media2nfo.log
  --loglevel:                 Logfile logging level, must be one of: DEBUG, INFO, WARN, ERROR
  --quiet:                    Only log errors to console
  --trace:                    Trace logging
  --version:                  Application Version

Environment Variables can be used to set:

Commands:
</pre>
<p><strong>Workflow</strong></p>
<p>And now the work flow for building and publishing our gem:</p>
<pre class="brush: plain; gutter: false;">
∴ rake spec
(in /Users/royw/views/example)
/Users/royw/.rvm/gems/ruby-1.8.7-p174/gems/jeweler-1.4.0/lib/jeweler/commands/check_dependencies.rb:13:Warning: Gem::Dependency#version_requirements is deprecated and will be removed on or after August 2010.  Use #requirement
All dependencies seem to be installed.
..

Finished in 0.004048 seconds

2 examples, 0 failures
 ∴ rake version:bump:patch
(in /Users/royw/views/example)
Current version: 0.0.0
Updated version: 0.0.1
 ∴ echo &quot;*.log&quot; &amp;gt;&amp;gt; .gitignore
 ∴ git status
# On branch master
# Changed but not updated:
#   (use &quot;git add ...&quot; to update what will be committed)
#
#       modified:   .gitignore
#       modified:   lib/example.rb
#       modified:   spec/example_spec.rb
#
# Untracked files:
#   (use &quot;git add ...&quot; to include in what will be committed)
#
#       Gemfile
#       bin/
#       example.gemspec
#       lib/VERSION
#       lib/example/
no changes added to commit (use &quot;git add&quot; and/or &quot;git commit -a&quot;)
 ∴ git add Gemfile bin example.gemspec lib .gitignore spec
 ∴ git status
# On branch master
# Changes to be committed:
#   (use &quot;git reset HEAD ...&quot; to unstage)
#
#       modified:   .gitignore
#       new file:   Gemfile
#       new file:   bin/example
#       new file:   example.gemspec
#       new file:   lib/VERSION
#       modified:   lib/example.rb
#       new file:   lib/example/cli.rb
#       new file:   lib/example/example.rb
#       modified:   spec/example_spec.rb
#
 ∴ git commit -m &quot;initial coding&quot;
Created commit 3aa37db: initial coding
 9 files changed, 208 insertions(+), 2 deletions(-)
 create mode 100644 Gemfile
 create mode 100755 bin/example
 create mode 100644 example.gemspec
 create mode 120000 lib/VERSION
 create mode 100644 lib/example/cli.rb
 create mode 100644 lib/example/example.rb
 ∴ rake build
(in /Users/royw/views/example)
Generated: example.gemspec
example.gemspec is valid.
WARNING:  no rubyforge_project specified
  Successfully built RubyGem
  Name: example
  Version: 0.0.1
  File: example-0.0.1.gem
</pre>
<p>And optionally if you are using github as your master and/or releasing to gemcutter:</p>
<pre class="brush: plain; gutter: false;">
 ∴ rake release
</pre>
<p><strong>Conclusion</strong></p>
<p>While we have just touched the surface of these powerful tools, hopefully I have shown that creating your project as a gem is pretty easy.  Just a few minor infrastructure enhancements and you&#8217;ve run out of excuses not to gemify your next project.  <img src='http://s2.wp.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Have fun,<br />
Roy</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/royw.wordpress.com/110/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/royw.wordpress.com/110/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/royw.wordpress.com/110/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/royw.wordpress.com/110/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/royw.wordpress.com/110/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/royw.wordpress.com/110/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/royw.wordpress.com/110/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/royw.wordpress.com/110/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/royw.wordpress.com/110/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/royw.wordpress.com/110/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/royw.wordpress.com/110/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/royw.wordpress.com/110/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/royw.wordpress.com/110/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/royw.wordpress.com/110/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=royw.wordpress.com&amp;blog=4979120&amp;post=110&amp;subd=royw&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://royw.wordpress.com/2010/06/07/ruby-gem-recipe/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/07aaa2c67e8b41fa38f628356a2b163a?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">royw</media:title>
		</media:content>
	</item>
		<item>
		<title>A Manager for DRb</title>
		<link>http://royw.wordpress.com/2009/07/15/a-manager-for-drb/</link>
		<comments>http://royw.wordpress.com/2009/07/15/a-manager-for-drb/#comments</comments>
		<pubDate>Wed, 15 Jul 2009 19:25:53 +0000</pubDate>
		<dc:creator>royw</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[drb]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://royw.wordpress.com/?p=75</guid>
		<description><![CDATA[The other day I wanted to parallelize one of my applications so dusted off DRb (included in ruby&#8217;s standard library). While DRb is pretty easy to use, the pain has always been setting up the servers and keeping their code current. This time I wanted the application to handle everything. So here are my basic [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=royw.wordpress.com&amp;blog=4979120&amp;post=75&amp;subd=royw&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>The other day I wanted to parallelize one of my applications so dusted off <a href="http://www.ruby-doc.org/stdlib/">DRb</a> (included in ruby&#8217;s standard library).  While DRb is pretty easy to use, the pain has always been setting up the servers and keeping their code current.  This time I wanted the application to handle everything.</p>
<p>So here are my basic requirements:</p>
<ul>
<li>Support using multiple DRb objects</li>
<li>Support local and remote hosting of the DRb server(s)</li>
<li>Application to install current code on host machine(s)</li>
<li>No application code to have to be manually installed on host machine(s)</li>
<li>Minimal software requirements on host machine(s)</li>
<li>Application to control the DRb server(s) (start/stop)</li>
<li>Linux and Mac support (don&#8217;t care about windows)</li>
</ul>
<p>The result is <a href="http://github.com/royw/drbman">Drbman</a>.<br />
<span id="more-75"></span></p>
<h4>Installing Drbman</h4>
<p>sudo gem install royw-drbman &#8211;source http://gems.github.com</p>
<h4>Using Drbman</h4>
<p>Let&#8217;s start by coding up the primes example (drbman/examples/primes).</p>
<p>I was wanting something simple for an example so thought the <a href="http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes">Sieve of Eratosthenes</a> would make a good one.  It turns out it&#8217;s not really a good candidate for parallelizing via DRb, but is still a good example.</p>
<p>After coding up the sieve and using the <a href="http://ruby-prof.rubyforge.org/">ruby-prof</a> profiler, the most time consuming chunk was the code that calculates the multiples of a prime which was taking 42% of the time, so I decided to parallelize it.  Moving it to it&#8217;s own class yields:</p>
<pre class="brush: ruby;">
class PrimeHelper
  # Find the multiples of the give prime number that are less than the
  # given maximum.
  # @example
  #  multiples_of(5,20) =&gt; [10, 15]
  # @param [Integer] prime the prime number to find the multiples of
  # @param [Integer] maximum the maximum integer
  # @return [Array] the array of the prime multiples
  def multiples_of(prime, maximum)
    a = []
    2.upto((maximum - 1) / prime) { |i| a &lt;&lt; (i * prime) }
    a
  end
end
</pre>
<p>After getting the PrimeHelper working in the original code, it&#8217;s time to make it into a DRb server:</p>
<pre class="brush: ruby;">
require 'drbman_server'

# A helper object for calculating primes using the Sieve of Eratosthenes
#
# == Usage
# ruby prime_helper.rb foo.example.com 1234
# will run the service as: druby://foo.example.com:1234
#
# ruby prime_helper.rb foo.example.com
# will run the service as: druby://foo.example.com:9000
#
# ruby prime_helper.rb
# will run the service as: druby://localhost:9000
#
class PrimeHelper
  include DrbmanServer

  # Find the multiples of the give prime number that are less than the
  # given maximum.
  # @example
  #  multiples_of(5,20) =&gt; [10, 15]
  # @param [Integer] prime the prime number to find the multiples of
  # @param [Integer] maximum the maximum integer
  # @return [Array&lt;Integer&gt;] the array of the prime multiples
  def multiples_of(prime, maximum)
    a = []
    2.upto((maximum - 1) / prime) { |i| a &lt;&lt; (i * prime) }
    a
  end
end

DrbmanServer.start_service(PrimeHelper)
</pre>
<p>As you can see, not much to it.  Add a <strong>require &#8216;drbman_server&#8217;</strong>, then a <strong>include DrbmanServer</strong> in your class, and finally start the service for your class with a <strong>DrbmanServer.start_service(<em>YourClassName</em>)</strong>.  One thing to note here is when we run the helper, it will not have a console connection (no stdin/stdout/stderr) so we would need to include some form of logger that supports alternate output such as syslog, file, email, etc. (personally I&#8217;m partial to the log4r gem).</p>
<p>Ok, now let&#8217;s see how we hook into the client application.  Here&#8217;s the sieve in our example:</p>
<pre class="brush: ruby;">
# The Sieve of Eratosthenes prime number finder
# Note, uses the Command design pattern
class SieveOfEratosthenes
  attr_reader :primes_elapse_time

  # Use the Sieve of Eratosthenes to find prime numbers
  #
  # @param [Integer] maximum find all primes lower than this maximum value - REQUIRED.
  # @option choices [Hash&lt;String,String&gt;] :dirs hash of local directories to copy to the host
  #   machines where key is local source and value is directory on host machine - REQUIRED.
  # @option choices [String] :run the name of the file to run on the host machine - REQUIRED.
  #  This file should start the drb server.  Note, this file will be daemonized before running.
  # @option choices [Array&lt;String&gt;] :hosts (['localhost']) array of host machine descriptions &quot;{user{:password}@}machine{:port}&quot;.
  # @option choices [Integer] :port (9000) default port number used to assign to hosts without a port number,
  #  the port number is incremented for each host.
  # @option choices [Array&lt;String&gt;] :gems array of gem names to verify are installed on the host machine.
  #  Note, 'daemons' is always added to this array.
  # @param [Logger] logger the logger to use
  def initialize(maximum, choices, logger)
    @maximum = maximum.to_i
    @choices = choices
    @logger = logger

    # we need at least one host that has a drb server running
    @choices[:hosts] = ['localhost'] if @choices[:hosts].blank?

    # specify the directories to copy to the host machine
    @choices[:dirs] = {File.join(File.dirname(__FILE__), '../drb_server') =&gt; 'drb_server'}

    # set the file to be ran that contains the drb server
    @choices[:run] = 'drb_server/prime_helper.rb' if @choices[:run].blank?

    # specify gems required by the drb server object
    # each host will be checked to make sure these gems are installed
    @choices[:gems] = ['log4r']

  end

  # Calculate the primes
  # @return [Array&lt;Integer] the primes in an Array
  def execute
    result = []
    @logger.debug { @choices.pretty_inspect }

    Drbman.new(@logger, @choices) do |drbman|
      @primes_elapse_time = elapse do
        result = primes(@maximum, drbman)
      end
    end
    result
  end

  private

  # recursive prime calculation
  # @param maximum (see #initialize)
  # @param [Drbman] drbman the drb manager instance
  # @return [Array&lt;Integer&gt;] the array of primes
  def primes(maximum, drbman)
    indices = []
    if maximum &gt; 2
      composites = calc_composites(maximum, drbman)
      flat_comps = composites.flatten.uniq
      indices = calc_indices(flat_comps, maximum)
    end
    indices
  end

  # find the composites array
  # @param maximum (see #initialize)
  # @param drbman (see #primes)
  # @return [Array&lt;Integer&gt;] the composites array
  def calc_composites(maximum, drbman)
    # when n = 20
    # sqr_primes = [2,3]
    # composites = [[2*2, 2*3, 2*4,...,2*9], [3*2, 3*3, 3*4,...,3*6]]
    sqr_primes = primes(Math.sqrt(maximum).to_i, drbman)
    composites = []
    threads = []
    mutex = Mutex.new
    sqr_primes.each do |ip|
      # parallelize via threads
      # then use the drb object within the thread
      threads &lt;&lt; Thread.new(ip, maximum) do |prime, max|
        drbman.get_object do |prime_helper|
          prime_multiples = prime_helper.multiples_of(prime, max)
          mutex.synchronize do
            composites &lt;&lt; prime_multiples
          end
        end
      end
    end
    threads.each {|thrd| thrd.join}
    composites
  end

  # sift the indices to find the primes
  # @param [Array&lt;Integer&gt;] flat_comps the flattened composites array
  # @param maximum (see #initialize)
  def calc_indices(flat_comps, maximum)
    indices = []
    flags = Array.new(maximum, true)
    flat_comps.each {|i| flags[i] = false}
    flags.each_index {|i| indices &lt;&lt; i if flags[i] }
    indices.shift(2)
    indices
  end

end
</pre>
<p>Let&#8217;s start with the <strong>@choices</strong>.  The example application uses <a href="http://user-choices.rubyforge.org">UserChoices</a> in cli.rb to handle command line, environment, and/or config file parameters.  UserChoices are close to a hash in that they have <strong>[]</strong> and <strong>[]=</strong> hash like methods.  If you don&#8217;t use UserChoices, then just pass the parameters in a regular Hash.</p>
<p>Back to the initializer, we first make sure at least one host is define in <strong>@choices[:hosts]</strong>.  Then we set the <strong>@choices[:dirs]</strong> key to the local directory that contains the files we want copied to each host machine and the value to the relative directory on the host machine we want the files copied into.  Next we point to our DRb server file with <strong>@choices[:run]</strong>.  Finally we specify any gems we want to make sure are installed on each host with the <strong>@choices[:gems]</strong> choice.</p>
<pre class="brush: ruby;">
    # we need at least one host that has a drb server running
    @choices[:hosts] = ['localhost'] if @choices[:hosts].blank?

    # specify the directories to copy to the host machine
    @choices[:dirs] = {File.join(File.dirname(__FILE__), '../drb_server') =&gt; 'drb_server'}

    # set the file to be ran that contains the drb server
    @choices[:run] = 'drb_server/prime_helper.rb' if @choices[:run].blank?

    # specify gems required by the drb server object
    # each host will be checked to make sure these gems are installed
    @choices[:gems] = ['log4r']
</pre>
<p>Next, take a look at the <strong>execute</strong> method.  Here&#8217;s where we run Drbman.  Note that we pass a Drbman instance into the primes method.</p>
<pre class="brush: ruby;">
    Drbman.new(@logger, @choices) do |drbman|
      @primes_elapse_time = elapse do
        result = primes(@maximum, drbman)
      end
    end
</pre>
<p>Finally skip down to the <strong>calc_composities</strong> method and inside the threading block:</p>
<pre class="brush: ruby;">
    sqr_primes.each do |ip|
      # parallelize via threads
      # then use the drb object within the thread
      threads &lt;&lt; Thread.new(ip, maximum) do |prime, max|
        drbman.get_object do |prime_helper|
          prime_multiples = prime_helper.multiples_of(prime, max)
          mutex.synchronize do
            composites &lt;&lt; prime_multiples
          end
        end
      end
    end
    threads.each {|thrd| thrd.join}
</pre>
<p>The <strong>drbman.get_object do |prime_helper|</strong> returns a DrbObject that references one of PrimeHelper instances on one of our host machines.  Yep, we don&#8217;t know which nor really care.  The threading handles the parallel execution.</p>
<p>And that pretty much covers how to use Drbman.</p>
<p>Oh, why is this example of the Sieve of Eratosthenes not a good fit for DRb parallelization?  It turns out that the return value from <strong>PrimeHelper.multiples_of</strong> is an Array that can grow quite large.  This has three problems.  Memory usage can get quite large, the network bandwidth used in returning the marshaled array, and that DRb appears to try to place the entire marshaled return object into a single packet.  While calculating primes of 10,000,000 finds 664,579 primes, trying with 20,000,000 generates the error &#8220;too large packet 51578437&#8243;.</p>
<p>So in choosing what to parallelize, it&#8217;s probably best to find parts with reasonable sized inputs and outputs.</p>
<h4>Running the Primes Example</h4>
<p>Using 3 machines:</p>
<ul>
<li>royw-macbook 2GHz Intel Core 2 Duo, 2GB</li>
<li>royw-gentoo 2.5GHz Intel Q9300, 4GB</li>
<li>dad-kubuntu 2.4GHz Intel Q6600, 4GB</li>
</ul>
<pre class="brush: bash;">
royw-macbook:primes royw$ bin/primes 10000000 -H 'royw-gentoo,royw-gentoo,royw-gentoo,royw-gentoo,dad-kubuntu,dad-kubuntu,dad-kubuntu,dad-kubuntu'
664579 primes found
calculation elapsed time: 49.745109
total elapsed time: 62.603881
royw-macbook:primes royw$ bin/primes 10000000 -H 'royw-gentoo,royw-gentoo,royw-gentoo,royw-gentoo'
664579 primes found
calculation elapsed time: 55.525954
total elapsed time: 58.73239
royw-macbook:primes royw$ bin/primes 10000000 -H 'royw-gentoo'
664579 primes found
calculation elapsed time: 107.114372
total elapsed time: 109.655318
royw-macbook:primes royw$ bin/primes 10000000 -H 'localhost'
664579 primes found
calculation elapsed time: 102.872163
total elapsed time: 107.949127
royw-macbook:primes royw$ bin/primes 10000000 -H 'localhost,localhost'
664579 primes found
calculation elapsed time: 68.445687
total elapsed time: 71.763174
</pre>
<p>The last two runs should raise an eyebrow or two.  The wire cost is pretty high.</p>
<h4>Internals</h4>
<p>There&#8217;s a little bit of the internals to Drbman that you really ought to be aware of.  </p>
<p>First, Drbman uses <a href="http://net-ssh.rubyforge.org/">net-ssh</a> to connect to each host machine.  It also uses <a href="http://net-ssh.rubyforge.org/scp/v1/api/classes/Net/SCP.html">net-scp</a> to copy files to the host machines.  This means you must be able to directly log into the host machine using ssh from your client machine (currently gateway sessions are not supported).  I&#8217;ve tested in two scenarios: passwordless certificates and normal password logins.</p>
<p>Next, Drbman will create a directory, <em>~/.drbman</em>, on each host and leave it.  Then each time your client uses the host, it will create a <em>~/.drbman/{uuid}</em> directory where it will upload your <strong>choices[:dirs]</strong> directories.  It will also create a controller file in this directory, <strong>&#8220;#{File.basename(@choices[:run],&#8217;.*&#8217;)}_controller.rb&#8221;</strong>, that is used to daemonize and control your DRb server.  This controller takes the normal daemon commands (start, stop, status) as well as command line parameters it should pass to your DRb server (after a double hyphen &#8216;&#8211;&#8217;).  The command line for our prime example is:</p>
<p>  ~/.drbman/{uuid} # ruby foo_controller start &#8212; example.com 1234</p>
<p>So basic setup flow is:</p>
<ol>
<li>open ssh connection to host machine</li>
<li>create <em>~/.drbman/{uuid}</em></li>
<li>scp <strong>@choices[:dirs]</strong> to <em>~/.drbman/{uuid}</em></li>
<li>create the controller in <em>~/.drbman/{uuid}</em></li>
<li>daemonize the <strong>@choices[:run]</strong> file by starting the controller</li>
</ol>
<p>The cleanup flow is:</p>
<ol>
<li>stop the DRb server on the host machine using DRbObject.stop_service</li>
<li>stop the daemon by issuing a controller stop command</li>
<li>delete the project&#8217;s directory (<em>~/.drbman/{uuid}</em>).</li>
</ol>
<p>Note, if you set <strong>@config[:leave]</strong> to true, then the project&#8217;s directory will not be deleted.  This can be useful for debugging.  I&#8217;ve also found this little script useful to gracefully stop any running daemons and delete all the project files in <em>~/.drbman</em>:</p>
<pre class="brush: ruby;">
#!/usr/bin/env ruby

# place this script in the ~/.drbman directory
# then use it to stop all daemons and delete
# all projects

Dir.glob(&quot;**/*_controller.rb&quot;).each do |controller|
  `ruby #{controller} stop`
  unless `ruby #{controller} status` =~ /running\s+\[pid\s\d+\]/
    `rm -rf #{File.dirname(controller)}`
  end
end
</pre>
<p>Have fun,<br />
Roy</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/royw.wordpress.com/75/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/royw.wordpress.com/75/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/royw.wordpress.com/75/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/royw.wordpress.com/75/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/royw.wordpress.com/75/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/royw.wordpress.com/75/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/royw.wordpress.com/75/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/royw.wordpress.com/75/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/royw.wordpress.com/75/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/royw.wordpress.com/75/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/royw.wordpress.com/75/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/royw.wordpress.com/75/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/royw.wordpress.com/75/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/royw.wordpress.com/75/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=royw.wordpress.com&amp;blog=4979120&amp;post=75&amp;subd=royw&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://royw.wordpress.com/2009/07/15/a-manager-for-drb/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/07aaa2c67e8b41fa38f628356a2b163a?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">royw</media:title>
		</media:content>
	</item>
		<item>
		<title>Katy Tea Party</title>
		<link>http://royw.wordpress.com/2009/07/05/katy-tea-party/</link>
		<comments>http://royw.wordpress.com/2009/07/05/katy-tea-party/#comments</comments>
		<pubDate>Sun, 05 Jul 2009 05:26:13 +0000</pubDate>
		<dc:creator>royw</dc:creator>
				<category><![CDATA[Liberty]]></category>

		<guid isPermaLink="false">http://royw.wordpress.com/?p=64</guid>
		<description><![CDATA[Attended the Katy Tea Party July 4th event.<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=royw.wordpress.com&amp;blog=4979120&amp;post=64&amp;subd=royw&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Howdy,</p>
<p>My wife, Lynna, and I attended the Katy Tea Party today.  For a rural county event, it was well attended.  I&#8217;m not good at estimating crowd sizes but will guess around a thousand.  The Katy Tea Party organization was pretty aggressive at getting an accurate head count (they counted the number of people driving in and did a registration) so hopefully they will publish the real number on their web site, <a href="http://katytea.com">http://katytea.com</a>, soon.<br />
<span id="more-64"></span><br />
<img src="http://royw.files.wordpress.com/2009/07/img_0070-jpg.png?w=300&#038;h=225" alt="IMG_0070.JPG" title="IMG_0070.JPG" width="300" height="225" class="alignnone size-medium wp-image-65" /></p>
<p>Being a libertarian, I was a little surprised that the Tea Party was not a fiscal conservative event.  Instead it was a religious right event, with all the evangelical violence propaganda.  This preference for using violence to solve problems such as immigration, abortion, and foreign policy are what drove me away from the Republican party (along with their talk the talk, but don&#8217;t bother walking the walk hypocrisy).  I really wished the Tea Party would have concentrated on old time fiscal conservatism versus the neo con, holier than thou flavour.</p>
<p><img src="http://royw.files.wordpress.com/2009/07/img_0065-jpg.png?w=300&#038;h=225" alt="IMG_0065.JPG" title="IMG_0065.JPG" width="300" height="225" class="alignnone size-medium wp-image-66" /></p>
<p>We arrived a little late so missed the first few speakers.  A Lt. Colonel was speaking about flying P-38s in WWII, but the PA system&#8217;s volume was a little too low.  The MC&#8217;s corrected the volume, then talked about the RagingElephant organization, which sounds like a &#8220;vote religious right&#8221; organization.</p>
<p><img src="http://royw.files.wordpress.com/2009/07/img_0067-jpg.png?w=300&#038;h=225" alt="IMG_0067.JPG" title="IMG_0067.JPG" width="300" height="225" class="alignnone size-medium wp-image-67" /></p>
<p>Next we listened to the excellent Kevin Black, who I&#8217;ll admit is one of my favourite local country western singers.</p>
<p><img src="http://royw.files.wordpress.com/2009/07/img_0064-jpg.png?w=300&#038;h=225" alt="IMG_0064.JPG" title="IMG_0064.JPG" width="300" height="225" class="alignnone size-medium wp-image-68" /></p>
<p>Next up was another Lt. Colonel from the Katy VFW.  Besides having trouble reading his speech, his message was a mixture of traditional values mixed with it&#8217;s good to use violence to spread American/Christian beliefs all delivered as a sermon.</p>
<p>The best speaker was a lady of Egyptian ancestry whose main message was we need to fight for our freedoms because they are worthy ones.  I don&#8217;t recall her quoting the bible.</p>
<p>Finally the MC&#8217;s got on their pulpit to deliver a call to arms sermon, quiet literally, complete with biblical references.</p>
<p>We finished the event listening to Kevin Black, among the few who stayed to the end.</p>
<p><img src="http://royw.files.wordpress.com/2009/07/img_0069-jpg.png?w=300&#038;h=225" alt="IMG_0069.JPG" title="IMG_0069.JPG" width="300" height="225" class="alignnone size-medium wp-image-69" /></p>
<p>Unfortunately the fireworks were cancelled, our guess is a burn ban, which was probably smart considering how dry the adjacent fields looked.</p>
<p>Overall, the event was mixed.  The call to elect principled fiscal conservatives is good, the need to hold public servants accountable is good, and the call to get the grass roots involved is good.  The call for using violence is major bad, but expected from christians.</p>
<p>In Liberty,<br />
Roy<br />
4 July 2009</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/royw.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/royw.wordpress.com/64/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/royw.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/royw.wordpress.com/64/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/royw.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/royw.wordpress.com/64/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/royw.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/royw.wordpress.com/64/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/royw.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/royw.wordpress.com/64/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/royw.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/royw.wordpress.com/64/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/royw.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/royw.wordpress.com/64/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=royw.wordpress.com&amp;blog=4979120&amp;post=64&amp;subd=royw&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://royw.wordpress.com/2009/07/05/katy-tea-party/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/07aaa2c67e8b41fa38f628356a2b163a?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">royw</media:title>
		</media:content>

		<media:content url="http://royw.files.wordpress.com/2009/07/img_0070-jpg.png?w=300" medium="image">
			<media:title type="html">IMG_0070.JPG</media:title>
		</media:content>

		<media:content url="http://royw.files.wordpress.com/2009/07/img_0065-jpg.png?w=300" medium="image">
			<media:title type="html">IMG_0065.JPG</media:title>
		</media:content>

		<media:content url="http://royw.files.wordpress.com/2009/07/img_0067-jpg.png?w=300" medium="image">
			<media:title type="html">IMG_0067.JPG</media:title>
		</media:content>

		<media:content url="http://royw.files.wordpress.com/2009/07/img_0064-jpg.png?w=300" medium="image">
			<media:title type="html">IMG_0064.JPG</media:title>
		</media:content>

		<media:content url="http://royw.files.wordpress.com/2009/07/img_0069-jpg.png?w=300" medium="image">
			<media:title type="html">IMG_0069.JPG</media:title>
		</media:content>
	</item>
		<item>
		<title>Code generators and git-rebase</title>
		<link>http://royw.wordpress.com/2009/06/18/code-generators-and-git-rebase/</link>
		<comments>http://royw.wordpress.com/2009/06/18/code-generators-and-git-rebase/#comments</comments>
		<pubDate>Thu, 18 Jun 2009 21:55:11 +0000</pubDate>
		<dc:creator>royw</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[rebase]]></category>

		<guid isPermaLink="false">http://royw.wordpress.com/?p=55</guid>
		<description><![CDATA[The classic example of using git-rebase is when a developer is working on their own branch while commits are made to the master branch, then the developer wants to sync up with the master branch, maybe as a precursor to merging the changes back into the master branch. Git&#8217;s rebase handles this elegantly by pushing [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=royw.wordpress.com&amp;blog=4979120&amp;post=55&amp;subd=royw&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>The classic example of using git-rebase is when a developer is working on their own branch while commits are made to the master branch, then the developer wants to sync up with the master branch, maybe as a precursor to merging the changes back into the master branch.  Git&#8217;s rebase handles this elegantly by pushing the changes on the developers branch, moving the point where the developers branch is forked from the master branch, then apply the saved changes to the new developers branch.<br />
<span id="more-55"></span><br />
<div id="attachment_57" class="wp-caption alignnone" style="width: 481px"><img src="http://royw.files.wordpress.com/2009/06/rebase-1.png?w=471&#038;h=297" alt="Classic Rebasing" title="rebase-1" width="471" height="297" class="size-full wp-image-57" /><p class="wp-caption-text">Classic rebase</p></div></p>
<p>For completeness, the command is:<br />
<code><br />
git rebase master developer_branch<br />
</code></p>
<p>Cool.</p>
<p>But for grins, let&#8217;s turn the problem around.  Let&#8217;s say that you use a code generator to jump start your project.  What usually happens is the code generators are only used until you invest time and effort into changing the code.  Wouldn&#8217;t it be nice to be able to use the code generators through out the development cycle?</p>
<p>The easiest way is to create a &#8216;generated&#8217; branch for the code generators to use then rebase to master.</p>
<div id="attachment_58" class="wp-caption alignnone" style="width: 435px"><img src="http://royw.files.wordpress.com/2009/06/rebase-2.png?w=425&#038;h=351" alt="Generator Rebasing" title="rebase-2" width="425" height="351" class="size-full wp-image-58" /><p class="wp-caption-text">Generator Rebasing</p></div>
<p>In this case the command would be:<br />
<code><br />
git rebase generated master<br />
</code></p>
<p>While you will have to manually refactor any name changes on the master branch introduced by the generated branch, you now have the option of using the generators over the life of the project.</p>
<p>Have fun,<br />
Roy</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/royw.wordpress.com/55/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/royw.wordpress.com/55/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/royw.wordpress.com/55/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/royw.wordpress.com/55/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/royw.wordpress.com/55/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/royw.wordpress.com/55/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/royw.wordpress.com/55/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/royw.wordpress.com/55/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/royw.wordpress.com/55/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/royw.wordpress.com/55/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/royw.wordpress.com/55/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/royw.wordpress.com/55/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/royw.wordpress.com/55/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/royw.wordpress.com/55/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=royw.wordpress.com&amp;blog=4979120&amp;post=55&amp;subd=royw&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://royw.wordpress.com/2009/06/18/code-generators-and-git-rebase/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/07aaa2c67e8b41fa38f628356a2b163a?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">royw</media:title>
		</media:content>

		<media:content url="http://royw.files.wordpress.com/2009/06/rebase-1.png" medium="image">
			<media:title type="html">rebase-1</media:title>
		</media:content>

		<media:content url="http://royw.files.wordpress.com/2009/06/rebase-2.png" medium="image">
			<media:title type="html">rebase-2</media:title>
		</media:content>
	</item>
		<item>
		<title>Specifications to Generated Merb Project</title>
		<link>http://royw.wordpress.com/2009/06/17/specifications-to-generated-merb-project/</link>
		<comments>http://royw.wordpress.com/2009/06/17/specifications-to-generated-merb-project/#comments</comments>
		<pubDate>Wed, 17 Jun 2009 04:50:25 +0000</pubDate>
		<dc:creator>royw</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[merb]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://royw.wordpress.com/?p=17</guid>
		<description><![CDATA[An experiment in generating a web application directly from rspec style specifications.<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=royw.wordpress.com&amp;blog=4979120&amp;post=17&amp;subd=royw&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<h4>Introduction</h4>
<p>While in the early requirements phase of a new open source project, I was wanting to encourage discussion on the database schema and web API.  So I wrote some <a href="http://rspec.info/">rspecs</a> to capture the models, their attributes, and their relationships.  As we played around with the schema, it was difficult to manually keep the specs accurate, particularly with the relationships.  So I wrote a script to generate a <a href="http://merbivore.com/index.html">merb</a> project from the rspecs to catch relationship problems.  This was good and also let me generate model and controller diagrams using <a href="http://github.com/royw/railroad_xing">railroad_xing</a>.<br />
<span id="more-17"></span><br />
After a few iterations I was happy and ready to add a little test data so I could play with the <a href="http://en.wikipedia.org/wiki/Restful">RESTful</a> routes as the web API.  I was still reluctant to give up the flexibility of generating the project directly from the rspecs, so after a couple of iterations, ended up with two features: first having a files/directory that is copied over the generated project; and second preserving changes by using git and generating the project on a branch then rebasing back to the master branch.</p>
<p>The first feature is simply to recursively to copy the structure in a files/ directory to the project directory after generation.  The provides replacement and addition capabilities.  Actually most of this stuff (rest controller,&#8230;) might ought to be refactored into slices.</p>
<p>One note on the RESTful route generation, it is limited to a depth of 2 (ex: /foos/1/bars/3) because the complexity of my original database would cause route compilation to run out of memory when the depth was set to 3.  You can edit the config/router.rb as needed.  And yes, the script replaces the normal merb generated controllers and views with ones that support REST, although the views are geared more towards debugging than a final product.</p>
<h4>Installation</h4>
<ol>
<li>Install git on your system (http://git-scm.com/)</li>
<li>Install ruby on your system (http://www.ruby-lang.org)</li>
<li>Install rubygems on your system (http://www.ruby-lang.org/en/libraries/)</li>
<li>Update rubygems (gem update system)</li>
<li>Install SQLite3 on your system (http://www.sqlite.org/)</li>
<li>Optionally install ImageMagick (http://www.imagemagick.org) recommended</li>
<li>Optionally install Curl (http://curl.haxx.se)</li>
<li>Optionally install RestClient (http://rest-client.googlecode.com)</li>
</ol>
<p>Now you have a choice, either just install the gem with:<br />
<code><br />
gem install royw-spec2merb<br />
</code><br />
or clone the repository:</p>
<ol>
<li>Change to the directory where you want to keep your repositories<br />
(ex: cd ~/views)</li>
<li>Clone the repository (git clone git://github.com/royw/spec2merb.git)<br />
If you are intended to commit changes to the spec2merb, then fork it<br />
and clone your fork instead.</li>
<li>Change to the repository directory (cd spec2merb)</li>
<li>Install required gems (refer to or run the install file)</li>
</ol>
<h4>So let&#8217;s generate a project</h4>
<p>First there will be two &#8220;project&#8221; directories:</p>
<ul>
<li>A project definition directory which will contain the spec/, and files/ directories used to create the merb project.  This should be under version control.</li>
<li>The created project directory which will be a git repository with two branches: &#8216;master&#8217; and &#8216;generated&#8217;.</li>
</ul>
<p>The basic work flow will be:</p>
<ol>
<li>Create the project definition directory.</li>
<li>Create the defining spec in the definition directory</li>
<li>Run the script to create the merb project.</li>
<li>Rebase master from generated branch.</li>
<li>Refine the spec until the created merb project runs</li>
<li>Examine the generated diagrams and classes</li>
<li>Edit the spec and repeat the creation until you are satisfied</li>
<li>Proceed with normal web app development on the &#8216;master&#8217; branch.</li>
</ol>
<p>As you can see from the work flow, all we are doing is adding a few iterations of using the project generation tools to a normal work flow.  The goal being to spin the project quickly, see what you got, then refine it until you are satisfied.</p>
<p>So let&#8217;s get started (note, this example is located in examples/addressbook).</p>
<p><code><br />
cd ~/views<br />
mkdir addressbook-definition<br />
cd addressbook-definition<br />
mkdir spec<br />
vi spec/addressbook-db-schema_spec.rb<br />
</code></p>
<p>And type into addressbook-db-schema_spec.rb:</p>
<pre class="brush: ruby;">
# This is a stub used to attach information about a model to the spec.
def synopsis(*args)
end
# NOTES
# * NVARCHAR should be used for fields that can contain non-english content.
# * Database should be configured for Unicode
describe(&quot;Locationbook Database Schema&quot;) do
  describe(&quot;Person Model&quot;) do
    synopsis(&quot;This model describes a person in the address book&quot;)
    # attributes
    it &quot;should have a name [TEXT]&quot;
    it &quot;should have a honorific [NVARCHAR(4)]&quot;
    # relationships
    it &quot;should have a relationship of zero or more companies [has 0:n Company]&quot;
    it &quot;should have a relationship of zero or more locations [has 0:n Location]&quot;
    it &quot;should have a relationship of zero or more phones [has 0:n Phone]&quot;
    it &quot;should have a relationship of zero or more emails [has n Email]&quot;
    # additional required methods
  end
  describe(&quot;Company Model&quot;) do
    synopsis(&quot;This model describes a company or business in the address book&quot;)
    # attributes
    it &quot;should have a name [NVARCHAR(255)]&quot;
    # relationships
    it &quot;should have a relationship of zero or more people [has 0:n Person]&quot;
    it &quot;should have a relationship of zero or more locations [has 0:n Location]&quot;
    it &quot;should have a relationship of zero or more phones [has 0:n Phones]&quot;
    it &quot;should have a relationship of zero or more emails [has n Email]&quot;
    # additional required methods
  end
  describe(&quot;Location Model&quot;) do
    synopsis(&quot;This model describes a mailing address or location.&quot;,
                  &quot;Note a bug in datamapper is letting me use the name that I would prefer, 'Address'&quot;)
    # attributes
    it &quot;should have a street_location (including apt or suite number) [NVARCHAR(255)]&quot;
    it &quot;should have a city [NVARCHAR(80)]&quot;
    it &quot;should have a state [NVARCHAR(2)]&quot;
    # relationships
    it &quot;should have a relationship of zero or more people [has 0:n Person]&quot;
    it &quot;should have a relationship of zero or more companies [has 0:n Company]&quot;
    # additional required methods
    it &quot;should return the full mailing location&quot;
  end
  describe(&quot;Phone Model&quot;) do
    synopsis(&quot;This model encapsulates phone numbers&quot;)
    # attributes
    it &quot;should have a number [NVARCHAR(14)]&quot;
    # relationships
    it &quot;should have a relationship of zero or more people [has 0:n Person]&quot;
    # it &quot;should have a relationship of zero or more companies [has 0:n Company]&quot;
    # additional required methods
    it &quot;should return the area code&quot;
    it &quot;should return the exchange&quot;
  end
  describe(&quot;Email Model&quot;) do
    synopsis(&quot;This model encapsulates email addresses&quot;)
    # attributes
    it &quot;should have an location [NVARCHAR(255)]&quot;
    # relationships
    it &quot;should have a relationship to a person [has 1 Person]&quot;
    it &quot;should have a relationship to a company [has 1 Company]&quot;
    # additional required methods
    it &quot;should return the account (ex: return 'foo' when location is 'foo@bar.com')&quot;
    it &quot;should return the host service (ex: return 'bar.com' when address is 'foo@bar.com')&quot;
  end
end
</pre>
<p>As you have probably guessed, the String parameter to the it() method has some key structures:</p>
<ul>
<li>&#8220;should have&#8221; denotes a model property</li>
<li>&#8220;should have a relationship&#8221; denotes an association</li>
<li>&#8220;should reference&#8221; denotes a belongs_to or a has 1 relationship</li>
<li>&#8220;should &#8230; variable_name [...]&#8221; or &#8220;should &#8230; variable_name (comment) [...]&#8221; defines the variable name and an optional comment.  The comment will be appended to the corresponding generated code.</li>
<li>what is in square brackets are database types (see lib/model_editor::to_dmtype for supported SQL types) or relationship types (has 1, has n, has 0..n, belongs_to).  The format is &#8220;[relationship model]&#8221; where the model is the class name of the model the relationship is with (see lib/spec2_merb::it for parsing details)</li>
</ul>
<p> Any it &#8220;&#8230;&#8221; line that does not match the rules is added as a comment to the model&#8217;s class.</p>
<p>The observant may have noticed that I commented out the company relationship in the Phone model.  That&#8217;s on purpose to demonstrated missing half of a relationship a little later.</p>
<p>OK now that we have a spec, let&#8217;s generate the project:<br />
<code><br />
spec2merb --project addressbook --spec spec/addressbook-db-schema_spec.rb<br />
</code></p>
<p>Hint, I like to put the above command line in a build script file so I don&#8217;t have keep retyping it&#8230;</p>
<p>It is a good practice to look carefully at the output for any error messages.  If you see any, then you can change the spec and generate again.</p>
<p>In this case the generation succeeds.  So first do a rebase:</p>
<p><code><br />
cd addressbook<br />
git rebase generated master<br />
</code></p>
<p>then fire up you favorite SVG viewer (I use <a href="http://www.mozilla.com">Firefox</a> for viewing and <a href="http://www.gimp.org/">GIMP</a> for printing) and open up the generated doc/models.svg (if you have <a href="http://www.imagemagick.org">ImageMagick</a> installed, then you should also have a doc/models.gif):</p>
<div id="attachment_22" class="wp-caption alignnone" style="width: 918px"><img class="size-full wp-image-22" title="models" src="http://royw.files.wordpress.com/2009/06/models.gif?w=908&#038;h=550" alt="Models Diagram Missing Phone Relationship to Company" width="908" height="550" /><p class="wp-caption-text">Models Diagram Missing Phone Relationship to Company</p></div>
<p>Notice that the CompanyPhone join model only has one connection to Company and none to the Phone model.  So go back and uncomment the company relationship in the Phone model spec and regenerate.  This time the models diagram should look like:</p>
<div id="attachment_24" class="wp-caption alignnone" style="width: 834px"><img class="size-full wp-image-24" title="models2" src="http://royw.files.wordpress.com/2009/06/models2.gif?w=824&#038;h=594" alt="Models Diagram" width="824" height="594" /><p class="wp-caption-text">Models Diagram</p></div>
<p>Looking good, but does it work?</p>
<h4>Smoke Test Time</h4>
<p>Change to the generated project directory and fire up merb<br />
<code><br />
cd addressbook<br />
bin/merb<br />
</code></p>
<p>Now fire up your browser and look at http://localhost:4000</p>
<div id="attachment_35" class="wp-caption alignnone" style="width: 769px"><img src="http://royw.files.wordpress.com/2009/06/addressbook-1.png?w=759&#038;h=768" alt="No route defined for /" title="Addressbook 1" width="759" height="768" class="size-full wp-image-35" /><p class="wp-caption-text">No route defined for /</p></div>
<p>While the script defined the rest routes, it did not define the home page route.  You can add the home page at the end of config/router.rb.  The title is the project name and the menu bar is the set of controllers (defined in config/init/app_config.rb).  For now click on People, Companies, Locations and you should see:</p>
<div id="attachment_36" class="wp-caption alignnone" style="width: 823px"><img src="http://royw.files.wordpress.com/2009/06/addressbook-2.png?w=813&#038;h=228" alt="Addressbook showing history" title="Addressbook 2" width="813" height="228" class="size-full wp-image-36" /><p class="wp-caption-text">Addressbook showing history</p></div>
<p>At the bottom of the page is the history navigation.</p>
<p>Fire up your <a href="http://curl.haxx.se/">curl</a> and get the people index:</p>
<p><code><br />
curl -H "Content-Type: text/xml; charset=UTF-8" http://localhost:4000/people.xml<br />
</code></p>
<p>Still not very interesting without any data.</p>
<h4>Add Some Data</h4>
<p>So let&#8217;s add some data via rest using curl. Note you may prefer using a rest client like the one at http://rest-client.googlecode.com</p>
<p>Let&#8217;s create a work area in the project definition directory:</p>
<p><code><br />
mkdir rest-requests<br />
</code></p>
<p>Now create the request xml by adding the following to rest-requests/add-person.xml:</p>
<pre class="brush: xml;">
&lt;person&gt;
  &lt;name&gt;Roy Wright&lt;/name&gt;
  &lt;honorific&gt;Mr.&lt;/honorific&gt;
&lt;/person&gt;
</pre>
<p>And lets run it:</p>
<p><code><br />
cd rest-requests<br />
curl -H "Content-Type: text/xml; charset=UTF-8" --data-binary @add-person.xml http://localhost:4000/people.xml<br />
</code></p>
<p>And look at the data again:</p>
<p><code><br />
curl -H "Content-Type: text/xml; charset=UTF-8" http://localhost:4000/people.xml<br />
</code></p>
<p>which should return:</p>
<pre class="brush: xml;">
&lt;people type='array'&gt;
  &lt;person&gt;
    &lt;id type='datamapper::types::serial'&gt;1&lt;/id&gt;
    &lt;name type='datamapper::types::text'&gt;Joe Bob&lt;/name&gt;
    &lt;honorific type='datamapper::types::text'/&gt;
  &lt;/person&gt;
&lt;/people&gt;
</pre>
<h4>From Spec to Web Service</h4>
<p>Wow!  What we have just done is created a web service directly from specs!  As a bonus we have a primitive HTML interface too.</p>
<p>Now admittedly all we have really done is exposed our models via REST.  We will probably want to add code to have the interface do something.  </p>
<h4>Adding Code</h4>
<p>Simply edit away on the master branch.  When you find you need a change in the database schema, simply commit your master branch, edit the db schema spec, regenerate using spec2merb, then rebase the generated branch back to master.</p>
<h4>Conclusion</h4>
<p>Overall I think the experiment is showing promise by defining the database schema in one location in a readable format (rspec).  Also by using git rebasing to extend using the merb generators over the entire project life is a major benefit.</p>
<h4>Where Next?</h4>
<p>I like the simple rspec organization of:  it &#8220;should&#8230;&#8221; statements.  These are pretty easy for anybody to grasp.  At the same time I do not like imposing structure on the string.  I&#8217;m thinking of extending the rspec DSL by adding a shall() method that would let me pass in the line to put in the model, something like:</p>
<pre class="brush: ruby;">
def shall(comment, model_line)
  'should ' + comment
end

describe('Foo Model') do
  synopsis('This is an example model')
  it shall('have a name attribute', 'property :name, String, :length =&amp;gt; 40, :unique =&amp;gt; true')
  it shall('reference several bars', 'has n, :bars, :through =&amp;gt; Resource')
end
</pre>
<p>Currently both sides of a relationship must be specified.  It would be nice to only have to spec a relationship once.  I&#8217;m not sure the best way to accomplish this as having the relationship defined in the model seems most natural and all I can think of is to have a separate describe block for all of the relationships.  Thoughts?</p>
<p>Finally what other parts of the application can we spec?  Maybe whether or not to use RESTful vs. ad hoc routes?  Maybe more on the views? Ideas?</p>
<p>Have Fun,<br />
Roy</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/royw.wordpress.com/17/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/royw.wordpress.com/17/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/royw.wordpress.com/17/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/royw.wordpress.com/17/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/royw.wordpress.com/17/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/royw.wordpress.com/17/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/royw.wordpress.com/17/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/royw.wordpress.com/17/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/royw.wordpress.com/17/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/royw.wordpress.com/17/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/royw.wordpress.com/17/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/royw.wordpress.com/17/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/royw.wordpress.com/17/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/royw.wordpress.com/17/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=royw.wordpress.com&amp;blog=4979120&amp;post=17&amp;subd=royw&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://royw.wordpress.com/2009/06/17/specifications-to-generated-merb-project/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/07aaa2c67e8b41fa38f628356a2b163a?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">royw</media:title>
		</media:content>

		<media:content url="http://royw.files.wordpress.com/2009/06/models.gif" medium="image">
			<media:title type="html">models</media:title>
		</media:content>

		<media:content url="http://royw.files.wordpress.com/2009/06/models2.gif" medium="image">
			<media:title type="html">models2</media:title>
		</media:content>

		<media:content url="http://royw.files.wordpress.com/2009/06/addressbook-1.png" medium="image">
			<media:title type="html">Addressbook 1</media:title>
		</media:content>

		<media:content url="http://royw.files.wordpress.com/2009/06/addressbook-2.png" medium="image">
			<media:title type="html">Addressbook 2</media:title>
		</media:content>
	</item>
	</channel>
</rss>
