<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">
  <channel>
    <title>play/type blog</title>
    <link>http://playtype.net</link>
    <language>en</language>
    <webMaster>me@someone.com (Someone)</webMaster>
    <copyright>Copyright 2009</copyright>
    <ttl>60</ttl>
    <pubDate>Mon, 13 Apr 2009 19:22:20 GMT</pubDate>
    <description />
    <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/play/type" type="application/rss+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><item>
      <title>So long, and thanks for all the fish!</title>
      <link>http://feedproxy.google.com/~r/play/type/~3/DU56ZE82zas/</link>
      <pubDate>Mon, 13 Apr 2009 19:18:00 GMT</pubDate>
      <guid isPermaLink="false">http://playtype.net/past/2009/4/13/so_long_and_thanks_for/</guid>
      <author>rany@playtype.net (rany)</author>
      <description>&lt;p&gt;boomloop is no more! Vielen Dank an alle, die bei uns mitgemacht haben: it was fun while it lasted!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/play/type/~4/DU56ZE82zas" height="1" width="1"/&gt;</description>
      <category domain="http://playtype.net/past/tags/boomloop">boomloop</category>
      <category domain="http://playtype.net/past/tags/closed">closed</category>
    <feedburner:origLink>http://playtype.net/past/2009/4/13/so_long_and_thanks_for/</feedburner:origLink></item>
    <item>
      <title>mini: an eventmachine clone of the irc bot they use at last.fm (in 140 lines)</title>
      <link>http://feedproxy.google.com/~r/play/type/~3/HR5oSZfKKIc/</link>
      <pubDate>Wed, 11 Mar 2009 22:57:00 GMT</pubDate>
      <guid isPermaLink="false">http://playtype.net/past/2009/3/11/mini_an_eventmachine_clone_of/</guid>
      <author>rany@playtype.net (rany)</author>
      <description>&lt;p&gt;last.fm uses old school irc to connect developers and business people inside the company. the last.fm founder wrote up an article about how they do this with the irccat bot.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&amp;#8230;Everyone that works at Last.fm is typically connected to our &lt;span class="caps"&gt;IRC&lt;/span&gt; server. We have different channels per team, as well as a company-wide channel, and a few channels dedicated to automated monitoring. Sometimes it makes much more sense to discuss / ask questions on &lt;span class="caps"&gt;IRC&lt;/span&gt; instead of email, and it&amp;rsquo;s useful to be able to raise people who are not in the office. That said, the main reason I&amp;rsquo;m writing this post is to mention the dev-support bot we use: irccat.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;a href="http://github.com/purzelrakete/mini/tree/master"&gt;i&amp;#8217;ve written a ruby eventmachine clone&lt;/a&gt; of the irc bot they wrote. it&amp;#8217;s called mini, and i tried to keep it as tiny as possible, which was fun :-). using mini&amp;#8230;&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&amp;#8230;you can easily send events to irc from shell scripts:&lt;/p&gt;&lt;/blockquote&gt;&lt;pre&gt;&lt;code&gt;echo "Ilovethistuffs yes .. i .. do. " | nc localhost 12345 &lt;br/&gt;vmstat | nc localhost 12345 &lt;br/&gt;echo "#musicteam,#legal,@alice New album uploaded: ..." | nc somemachine 12345 &lt;br/&gt;tail -f /var/log/important.log | nc somemachine 12345&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;you can also send commands from inside the channels using `?command` syntax. this will invoke a script called minictl on your $PATH.&lt;/p&gt;&lt;p&gt;you can also post to the bot over a url. there&amp;#8217;s simple mechanism for adding plugins, as well.&lt;/p&gt;&lt;p&gt;it&amp;#8217;s a neat little toy, &lt;a href="http://github.com/purzelrakete/mini/tree/master"&gt;check out the source on github&lt;/a&gt;.&lt;/p&gt;&lt;h2&gt;Installation&lt;/h2&gt;&lt;pre&gt;&lt;code&gt;sudo gem install purzelrakete-mini --source=http://gems.github.com&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;create a directory for mini and run the following&lt;/p&gt;&lt;pre&gt;&lt;code&gt;minigen create &lt;your-mini-dir&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;this creates two files in your mini dir. edit both as you see fit, then add your mini dir to your $PATH inside of your shell&amp;#8217;s rc file.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/play/type/~4/HR5oSZfKKIc" height="1" width="1"/&gt;</description>
      <category domain="http://playtype.net/past/tags/bot">bot</category>
      <category domain="http://playtype.net/past/tags/eventmachine">eventmachine</category>
      <category domain="http://playtype.net/past/tags/github">github</category>
      <category domain="http://playtype.net/past/tags/irc">irc</category>
      <category domain="http://playtype.net/past/tags/lastfm">lastfm</category>
      <category domain="http://playtype.net/past/tags/mini">mini</category>
      <category domain="http://playtype.net/past/tags/ruby">ruby</category>
    <feedburner:origLink>http://playtype.net/past/2009/3/11/mini_an_eventmachine_clone_of/</feedburner:origLink></item>
    <item>
      <title>Check out balsamiq Mockups. It's an excellent alternative to wireframing tools</title>
      <link>http://feedproxy.google.com/~r/play/type/~3/eL-SpQmAThI/</link>
      <pubDate>Wed, 31 Dec 2008 15:23:00 GMT</pubDate>
      <guid isPermaLink="false">http://playtype.net/past/2008/12/31/balsamiq_mockups_is_the_best_interface_prototyping_tool_ive_seen/</guid>
      <author>rany@playtype.net (rany)</author>
      <description>&lt;p&gt;I discovered balsamiq mockups a few weeks ago. It&amp;#8217;s a prototyping too with which you can sketch out user interfaces. it&amp;#8217;s very pleasant to use, and the results are excellent input for product development.&lt;/p&gt;&lt;p&gt;Balsamiq is good for keeping the discussion at a sketch level. The result is clearly not a finished design, which promotes more meaningful discussions about changes.&lt;/p&gt;&lt;p&gt;check it out! it&amp;#8217;s a one man effort, which i find very impressive.&lt;/p&gt;&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/aJTuFRaIi_g&amp;#38;hl=de&amp;#38;fs=1"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/aJTuFRaIi_g&amp;#38;hl=de&amp;#38;fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;img src="http://feeds.feedburner.com/~r/play/type/~4/eL-SpQmAThI" height="1" width="1"/&gt;</description>
      <category domain="http://playtype.net/past/tags/balsamiq">balsamiq</category>
      <category domain="http://playtype.net/past/tags/mockups">mockups</category>
      <category domain="http://playtype.net/past/tags/prototyping">prototyping</category>
    <feedburner:origLink>http://playtype.net/past/2008/12/31/balsamiq_mockups_is_the_best_interface_prototyping_tool_ive_seen/</feedburner:origLink></item>
    <item>
      <title>Writing Linux desktop apps with Ruby and KDE: Hello World</title>
      <link>http://feedproxy.google.com/~r/play/type/~3/jfRWi4TBRtU/</link>
      <pubDate>Sat, 13 Dec 2008 10:57:00 GMT</pubDate>
      <guid isPermaLink="false">http://playtype.net/past/2008/12/13/writing_linux_desktop_apps_with_ruby_and_kde/</guid>
      <author>rany@playtype.net (rany)</author>
      <description>&lt;p&gt;It&amp;#8217;s possible to write Linux desktop apps using ruby! All you need is a distribution running &lt;span class="caps"&gt;KDE 4&lt;/span&gt;.2. I have &lt;a href="http://playtype.net/past/2008/12/11/installing_kubuntu_with_kde42_on_vmware_fusion/"&gt;Kubuntu running on my Macbook.&lt;/a&gt;&lt;/p&gt;&lt;p&gt;First off, get the Ruby bindings installed: &lt;/p&gt;&lt;pre&gt;&lt;code&gt;apt-get install ruby-kde4&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next up, create a project directory for your Plasmoid. You can follow along with &amp;#8216;Rakete&amp;#8217;, which is my attempt at a Twitter client. The only thing Rakete does so far is to load and display an &lt;span class="caps"&gt;SVG&lt;/span&gt;.&lt;/p&gt;&lt;p&gt;&lt;img src="http://playtype.net/images/rakete-tree.jpg" alt="" /&gt;&lt;/p&gt;&lt;p style="clear: left;"&gt;you can call your root directory anything you like. In my case, it&amp;#8217;s &amp;#8216;application&amp;#8217; instread of &amp;#8216;rakete&amp;#8217;.&lt;/p&gt;&lt;p&gt;The `contents`directory holds the main files in a number of subdirectories. The most important of these is `code`, which, you guessed it, holds your ruby entry point, `main.rb`.  `images` holds the &lt;span class="caps"&gt;SVG&lt;/span&gt; we want to render. You can create SVGs with a number of tools such as Adobe Illustrator or &lt;a href="http://www.inkscape.org/"&gt;Inkscape&lt;/a&gt; (open source). &lt;/p&gt;&lt;pre&gt;&lt;code&gt;require 'plasma_applet'&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;module Rakete&lt;br/&gt;  class Main &amp;lt; PlasmaScripting::Applet&lt;br/&gt;end&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;def initialize(parent, args = nil)&lt;br/&gt;    super&lt;br/&gt;  end&lt;br/&gt;end&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;def init&lt;br/&gt;  @svg = Plasma::Svg.new(self)&lt;br/&gt;  @svg.imagePath = 'widgets/rakete'&lt;br/&gt;end&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;def paintInterface(painter, option, contentsRect)&lt;br/&gt;  @svg.resize(size())&lt;br/&gt;  @svg.paint(painter, 0, 0)&lt;br/&gt;end&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;`main.rb`should contain a class called `Main`, inside of a module named after your application. There are a number of callbacks you have to implement.&lt;/p&gt;&lt;p&gt;First off, you have &amp;igrave;nit`, which is called when the applet is first loaded. In here, we construct Plasma::Svg, which loads up rakete.svg.&lt;/p&gt;&lt;p&gt;SVGs are read from the svg.imagePath. This is relative to the $KDE_ROOT/apps/desktoptheme/default. &lt;span class="caps"&gt;KDE&lt;/span&gt;_ROOT varies from system to system. In the case of Kubuntu, it is /usr/share/kde4. So specifying an svg image path of &lt;/p&gt;&lt;pre&gt;&lt;code&gt;@svg.imagePath = 'widgets/rakete'&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;will mean that the svg is searched for under &lt;/p&gt;&lt;pre&gt;&lt;code&gt;/usr/share/kde4/apps/desktoptheme/default/widgets/rakete.svg&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This lies outside of your Plasmoid directory structure, so the SVGs you package have to be staged into  the widgets directory. Currently I don&amp;#8217;t see any way of automating this staging process, so you&amp;#8217;ll have to copy the svg from images/ over to the widgets directory. I&amp;#8217;ll post more info as soon as I get it.&lt;/p&gt;&lt;p&gt;&lt;span class="caps"&gt;UPDATE 25&lt;/span&gt;.12.08: you &lt;strong&gt;can&lt;/strong&gt; reference images inside of your plasmoid directory structure in your code. it turns out that doing so is very easy. applets have a `package` method, which returns an object representing your plasmoid package. the package object can resolve the canonical filename of your svg like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;@svg.imagePath = package.file_path("images", "rakete.svg")&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;now you don&amp;#8217;t need to worry about staging anything.&lt;/p&gt;&lt;p&gt;Next, create the metadata.desktop file: &lt;/p&gt;&lt;pre&gt;&lt;code&gt;[Desktop Entry]&lt;br/&gt;Name=Rakete&lt;br/&gt;Comment=an attempt at a better twitter plasmoid using ruby. &lt;br/&gt;Icon=&lt;br/&gt;Type=Service&lt;br/&gt;ServiceTypes=Plasma/Applet&lt;br/&gt;X-Plasma-API=ruby-script&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;X-KDE-PluginInfo-Author=Rany Keddo&lt;br/&gt;X-KDE-PluginInfo-Email=purzelrakete@gmail.com    &lt;br/&gt;X-KDE-PluginInfo-Name=rakete&lt;br/&gt;X-KDE-PluginInfo-Version=pre0.1    &lt;br/&gt;X-KDE-PluginInfo-Website=http://playtype.net&lt;br/&gt;X-KDE-PluginInfo-Category=&lt;br/&gt;X-KDE-PluginInfo-Depends=&lt;br/&gt;X-KDE-PluginInfo-License=MIT&lt;br/&gt;X-KDE-PluginInfo-EnabledByDefault=true&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Replace the pertinent field values.&lt;/p&gt;&lt;p&gt;Finally, you&amp;#8217;ll want to run this thing. Run the following above the plasma root directory:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;plasmapkg --upgrade application&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will install the Plasmoid on your system, or update it is already installed. Note that if you plasmoid root directory has a different name to &amp;#8220;application&amp;#8221;, you&amp;#8217;ll need to cahnge the packaging command accordingly.&lt;/p&gt;&lt;p&gt;Finally, open the cashew in the top righthand corner of your desktop and select &amp;#8220;Add Widget&amp;#8221;. You&amp;#8217;ll find your widget in the list.&lt;/p&gt;&lt;p&gt;As it stands, Plasma will sometimes crash when running this Applet. I&amp;#8217;ll pass this information on to Richard Dale, the maintainer of the Ruby bindings. Hopefully he will be able to locate problems in the runtime, which is still in Beta1.&lt;/p&gt;&lt;p&gt;Richard was also super helpful in getting this example to run.  Thank you Richard :-)&lt;/p&gt;&lt;p&gt;I&amp;#8217;ll write more posts as I make progress with the Twitter client. You can &lt;a href="http://github.com/purzelrakete/rakete/tree/master"&gt;find Rakete on Github.&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/play/type/~4/jfRWi4TBRtU" height="1" width="1"/&gt;</description>
      <category domain="http://playtype.net/past/tags/kde">kde</category>
      <category domain="http://playtype.net/past/tags/plasma">plasma</category>
      <category domain="http://playtype.net/past/tags/ruby">ruby</category>
    <feedburner:origLink>http://playtype.net/past/2008/12/13/writing_linux_desktop_apps_with_ruby_and_kde/</feedburner:origLink></item>
    <item>
      <title>Sending mail asynchronously in Rails using Workling </title>
      <link>http://feedproxy.google.com/~r/play/type/~3/v1xhy0FADKI/</link>
      <pubDate>Tue, 11 Nov 2008 10:08:00 GMT</pubDate>
      <guid isPermaLink="false">http://playtype.net/past/2008/11/11/sending_mail_asynchronously_in_rails/</guid>
      <author>rany@playtype.net (rany)</author>
      <description>&lt;p&gt;If you want to send mail asynchronously in your rails app, but don&amp;#8217;t want to bother with using a queue server like Starling, then have a go at this&amp;#8230;&lt;/p&gt;&lt;p&gt;First, run this: &lt;/p&gt;&lt;pre&gt;&lt;code&gt;./script/plugin install git://github.com/purzelrakete/workling.git&lt;br/&gt;./script/plugin install git://github.com/langalex/workling_mailer.git&lt;br/&gt;./script/plugin install git://github.com/matthewrudy/rudeq.git&lt;br/&gt;rake queue:setup&lt;br/&gt;rake db:migrate&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;now add this to your environment: &lt;/p&gt;&lt;pre&gt;&lt;code&gt;Workling::Clients::MemcacheQueueClient.memcache_client_class = RudeQ::Client&lt;br/&gt;Workling::Remote.dispatcher = Workling::Remote::Runners::ClientRunner.new&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, add this into the body of your mailer classes:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;include AsynchMail&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;now start the workling client: &lt;/p&gt;&lt;pre&gt;&lt;code&gt;./script/workling_client start&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;done!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/play/type/~4/v1xhy0FADKI" height="1" width="1"/&gt;</description>
      <category domain="http://playtype.net/past/tags/asynchronous">asynchronous</category>
      <category domain="http://playtype.net/past/tags/mail">mail</category>
      <category domain="http://playtype.net/past/tags/rails">rails</category>
      <category domain="http://playtype.net/past/tags/rudeq">rudeq</category>
      <category domain="http://playtype.net/past/tags/workling">workling</category>
    <feedburner:origLink>http://playtype.net/past/2008/11/11/sending_mail_asynchronously_in_rails/</feedburner:origLink></item>
    <item>
      <title>Workling 0.4.1 is out, now with RabbitMQ support and simple extensions</title>
      <link>http://feedproxy.google.com/~r/play/type/~3/xAwAqQGrWS4/</link>
      <pubDate>Fri, 07 Nov 2008 19:31:00 GMT</pubDate>
      <guid isPermaLink="false">http://playtype.net/past/2008/11/7/workling_0_4_0_is_out_with_rabbitmq_ampq_support/</guid>
      <author>rany@playtype.net (rany)</author>
      <description>&lt;p&gt;I did some refactoring this week, and the result is a &lt;a href="http://github.com/purzelrakete/workling/tree/master"&gt;much improved workling&lt;/a&gt;. Adding new work brokers is a snap now. I&amp;#8217;ll show you how I added RabbitMQ, so that you can go about adding your own brokers. So without further ado&amp;#8230;&lt;/p&gt;

&lt;h2&gt;RabbitMQ, a quick introduction&lt;/h2&gt;

&lt;p&gt;A lot of Ruby people have been talking about using RabbitMQ as their Queue of choice. Soundcloud.com are using it, as is new bamboo founder Johnathan Conway, who is using it at his video startup http://www.vzaar.com/. He says: &lt;/p&gt;

&lt;blockquote&gt;
    &lt;p&gt;RabbitMQ &amp;ndash; Now this is the matrons knockers when it comes to kick ass, ultra fast and scalable messaging. It simply rocks, with performance off the hook. It&amp;rsquo;s written in Erlang and supports the AMPQ protocol. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="http://playtype.net/past/2008/10/9/installing_rabbitmq_on_osx/"&gt;Follow the instructions here&lt;/a&gt; to get this beauty installed.&lt;/p&gt;

&lt;h2&gt;Adding Ampq to Workling&lt;/h2&gt;

&lt;p&gt;There are two new base classes you can extend to add new brokers. I&amp;#8217;ll describe how this is done, but the code i show is already a part of workling. Skip to &amp;#8216;activating amqp in your application&amp;#8217; to see how this is activated.&lt;/p&gt;

&lt;h3&gt;Clients&lt;/h3&gt;

&lt;p&gt;Clients help workling to connect to job brokers. To add an AmqpClient, we need to extend from &lt;code&gt;Workling::Client::Base&lt;/code&gt; and implement a couple of methods. &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;require 'workling/clients/base'
require 'mq'

#
#  An Ampq client
#
module Workling
  module Clients
    class AmqpClient &amp;lt; Workling::Clients::Base

      # starts the client. 
      def connect
        @amq = MQ.new
      end

      # stops the client.
      def close
        @amq.close
      end

      # request work
      def request(queue, value)
        @amq.queue(queue).publish(value)
      end

      # retrieve work
      def retrieve(queue)
        @amq.queue(queue)
      end

      # subscribe to a queue
      def subscribe(queue)
        @amq.queue(queue).subscribe do |value|
          yield value
        end
      end

    end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Were&amp;#8217;s using the eventmachine amqp client for this, you can find it &lt;a href="http://github.com/tmm1/amqp/tree/master"&gt;up on github&lt;/a&gt;. &lt;code&gt;connect&lt;/code&gt; and &lt;code&gt;close&lt;/code&gt; do exactly what it says on the tin: connecting to rabbitmq and closing the connection. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;request&lt;/code&gt; and &lt;code&gt;retrieve&lt;/code&gt; are responsible for placing work on rabbitmq. The methods are passed the correct queue, and a value that contains the worker method arguments. If you need control over the queue names, look at the RDoc for Workling::Routing::Base. In our case, there&amp;#8217;s no special requirement here. &lt;/p&gt;

&lt;p&gt;Finally, we implement a &lt;code&gt;subscribe&lt;/code&gt; method. Use this if your broker supports callbacks, as is the case with amqp. This method expects to a block, which we pass into the amqp subscribe method here. The block will be called when a message is available on the queue, and the result is yielded into the block. &lt;/p&gt;

&lt;p&gt;Having subscription callbacks is very nice, because this way, we don&amp;#8217;t need to keep calling &lt;code&gt;get&lt;/code&gt; on the queue to see if something new is waiting. &lt;/p&gt;

&lt;p&gt;So now we&amp;#8217;re done! That&amp;#8217;s all you need to add RabbitMQ to workling. Configure it in your application as descibed below. &lt;/p&gt;

&lt;h3&gt;Invokers&lt;/h3&gt;

&lt;p&gt;There&amp;#8217;s still potential to improve things though. Workling 0.4.0 introduces the idea of invokers. Invokers grab work off a job broker, using a client (see above). They subclass Workling::Remote::Invokers::Base. Read the RDoc for a description of the methods. &lt;/p&gt;

&lt;p&gt;Workling comes with a couple of standard invokers, like the BasicPoller. This invoker simply keeps hitting the broker every n seconds, checking for new work and executing it immediately. The ThreadedInvoker does the same, but spawns a Thread for every Worker class the project defines. &lt;/p&gt;

&lt;p&gt;So Amqp: it would be nice if we had an invoker that makes use of the subscription callbacks. Easily done, lets have a look: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;require 'eventmachine'
require 'workling/remote/invokers/base'

#
#  Subscribes the workers to the correct queues. 
# 
module Workling
  module Remote
    module Invokers
      class EventmachineSubscriber &amp;lt; Workling::Remote::Invokers::Base

        def initialize(routing, client_class)
          super
        end

        #
        #  Starts EM loop and sets up subscription callbacks for workers. 
        #
        def listen
          EM.run do
            connect do
              routes.each do |queue|
                @client.subscribe(queue) do |args|
                  run(queue, args)
                end
              end
            end
          end
        end

        def stop
          EM.stop if EM.reactor_running?
        end
      end
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Invokers have to implement two methods, &lt;code&gt;listen&lt;/code&gt; and &lt;code&gt;stop&lt;/code&gt;. Listen starts the main listener loop, which is responsible for starting work when it becomes available. &lt;/p&gt;

&lt;p&gt;In our case, we need to start an EM loop around &lt;code&gt;listen&lt;/code&gt;. This is because the Ruby AMQP library needs to run inside of an eventmachine reactor loop. &lt;/p&gt;

&lt;p&gt;Next, inside of &lt;code&gt;listen&lt;/code&gt;, we need to iterate through all defined routes. There is a route for each worker method you defined in your application. The routes double as queue names. For this, you can use the helper method &lt;code&gt;routes&lt;/code&gt;. Now we attach a callback to each queue. We can use the helper method &lt;code&gt;run&lt;/code&gt;, which executes the worker method associated with the queue, passing along any supplied arguments. &lt;/p&gt;

&lt;p&gt;That&amp;#8217;s it! We now have a more effective Invoker, which we can activate in our application like this&amp;#8230;&lt;/p&gt;

&lt;h2&gt;Configuring your application to use AMQP (with RabbitMQ)&lt;/h2&gt;

&lt;p&gt;Again, &lt;a href="http://playtype.net/past/2008/10/9/installing_rabbitmq_on_osx/"&gt;follow the instructions here&lt;/a&gt; to get RabbitMQ running on your OSX development machine. &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Workling::Remote.invoker = Workling::Remote::Invokers::EventmachineSubscriber
Workling::Remote.dispatcher = Workling::Remote::Runners::ClientRunner.new
Workling::Remote.dispatcher.client = Workling::Clients::AmqpClient.new
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;enjoy!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/play/type/~4/xAwAqQGrWS4" height="1" width="1"/&gt;</description>
      <category domain="http://playtype.net/past/tags/amqp">amqp</category>
      <category domain="http://playtype.net/past/tags/asynchronous">asynchronous</category>
      <category domain="http://playtype.net/past/tags/background">background</category>
      <category domain="http://playtype.net/past/tags/erlang">erlang</category>
      <category domain="http://playtype.net/past/tags/process">process</category>
      <category domain="http://playtype.net/past/tags/queue">queue</category>
      <category domain="http://playtype.net/past/tags/rabbitmq">rabbitmq</category>
      <category domain="http://playtype.net/past/tags/rails">rails</category>
      <category domain="http://playtype.net/past/tags/workling">workling</category>
    <feedburner:origLink>http://playtype.net/past/2008/11/7/workling_0_4_0_is_out_with_rabbitmq_ampq_support/</feedburner:origLink></item>
    <item>
      <title>Ruby and Bash FTW</title>
      <link>http://feedproxy.google.com/~r/play/type/~3/FdEEItqrxIk/</link>
      <pubDate>Thu, 23 Oct 2008 16:26:00 GMT</pubDate>
      <guid isPermaLink="false">http://playtype.net/past/2008/10/23/ruby_and_bash_ftw/</guid>
      <author>rany@playtype.net (rany)</author>
      <description>&lt;p&gt;here&amp;#8217;s a little thing that made me happy today. i spent some time automating pdf printing. i have a directory structure like so: receipts/2008/10. when needed, everything is packed into a single pdf and sent to the printer down the road. to do this, every file has to be converted to pdf and combined to a single file. input formats are rtf, txt, pdf or multipage tiff. &lt;/p&gt;

&lt;p&gt;so today wrote a dirty ruby script for this.  mashing little unix tools into your ruby code is good. dirty, trash-eating shell opening code, i salute you! it&amp;#8217;s taken me a while to come around to this, being the ex-java i am. 2 years away from jcp.org seem to have done me good ;)&lt;/p&gt;

&lt;h2&gt;Low effort pdf conversion using the shell&lt;/h2&gt;

&lt;p&gt;&lt;a href="http://bigcurl.net"&gt;sam&lt;/a&gt;, suggested i should look into ruby-cocoa, since preview can print many formats to pdf. but a bit of googling lead me to CUPS-PDF, with which you can set up a printer which dumps pdfs into a specified directory. Follow along at http://www.codepoetry.net/projects/cups-pdf-for-mosx. &lt;/p&gt;

&lt;p&gt;So lets see if we have everything we need to get started. First off, lets check if the CUPS printer has been installed: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    raise Exception.new("install cups.") unless `lpstat -a`.grep Regexp.new(CUPS_PRINTER)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;lpstat -a gives you a list of all printing devices configured on your machine. We&amp;#8217;re going to search this for CUPS_PRINTER using grep. We&amp;#8217;ll actually use this printer to convert input files, like rtfs, into pdf files on disk. &lt;/p&gt;

&lt;p&gt;So now we want to Grab the files and pass them to the printer. This should all be automatic. A quick search reveals&amp;nbsp;&lt;code&gt;lp&lt;/code&gt;: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    `lp -d #{ CUPS_PRINTER } #{ file }`
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Nice, this now dumps the converted file into the CUPS root directory, which by default is cups-pdf on your desktop. &lt;/p&gt;

&lt;h3&gt;Multipage Tiffs&lt;/h3&gt;

&lt;p&gt;Unfortunately lp only prints the first page of multipage TIFFS to pdf. So what we need to to do is to extract the pages from the tiff. Lets see if there&amp;#8217;s anything available: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    man -k tiff
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This shows us tiffutil, which can be utilized to &amp;#8216;manipulate tiff images&amp;#8217;. Lets see how this works: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    man tiffutil
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Bingo, there&amp;#8217;s an -extract option which pulls out the specified page. So now we just need to know how many pages are in the tiff. There&amp;#8217;s an -info option that gives us the information we need. Each image is described with an entry that starts with &amp;#8220;Directory at&amp;#8221;. Lets count the number of times this appears: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;     `tiffutil -info #{ file } | grep "Directory at" | wc -l`.strip.to_i
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here we&amp;#8217;re piping the output of tiffutil to grep, which results in a line per found image. We pipe this through wc -l to count the number of images. &lt;/p&gt;

&lt;h2&gt;Combining the pdf&lt;/h2&gt;

&lt;p&gt;Thanks again to &lt;a href="http://bigcurl.net"&gt;sam&lt;/a&gt; for pointing me to pdfcombine. just specify all files you want to combine, the outfile with -out, and you&amp;#8217;re done. So let&amp;#8217;s see if it&amp;#8217;s installed&amp;#8230;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    raise Exception.new("install pdfcombine command line tool. ") unless `which pdfcombine`
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;which&lt;/code&gt; searches $PATH for the specified executable. The full path is returned. To run it: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;     `pdfcombine #{ exports.join(" ") } -o #{ packfile }`
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And you&amp;#8217;re done!&lt;/p&gt;

&lt;h2&gt;The script&lt;/h2&gt;

&lt;pre&gt;&lt;code&gt;    require 'fileutils'
    require 'logger'

    #
    #  Combines all txt, rtf, tiff multipage, pdf files in a named subdirectory. 
    #  Results in a single pdf called &amp;lt;subdir&amp;gt;-pack.pdf 
    #
    class ReceiptsPrinter

      # requires you to be in this subdirectory for safety reasons (FileUtils.rm_r is used.)
      RECEIPTS_ROOT = "/Users/me/Documents/2008"

      # cups prints to this directory
      CUPS_ROOT = "/Users/me/Desktop/cups-pdf"

      # cups printer name
      CUPS_PRINTER = "CUPS_PDF"

      # 
      #  pass in the subdirectory name which includes the files
      #
      def initialize(subdir)
        @subdir = subdir
        @files = Dir.glob("#{ subdir }/*.*")
        @export_dir = "#{ subdir }/export/"
        @logger = Logger.new(STDOUT)

        # please meet the conditions.   
        raise Exception.new("specify the subdir to process.") unless @subdir
        raise Exception.new("cd to #{ RECEIPTS_ROOT } before starting.") if `pwd`.chomp != RECEIPTS_ROOT
        raise Exception.new("install pdfcombine command line tool. ") unless `which pdfcombine`
        raise Exception.new("install cups to enable conversions: http://www.codepoetry.net/projects/cups-pdf-for-mosx ") unless `lpstat -a`.grep Regexp.new(CUPS_PRINTER)

        # sets up the export directory
        FileUtils.rm_r @export_dir rescue nil
        FileUtils.mkdir_p @export_dir
      end

      #
      #  iterates through all files in subdirectory, converting them to pdf if necessary, then combines them
      #  into a single pdf. 
      #
      def print
        @files.each { |file| convert(file) }
        packfile = combine

        @logger.info "Done! Packed it up into #{ packfile }."
      end

      private
        def convert(file)
          case type(file)
          when "pdf" : stage(file)
          when "tiff" 
            extract_tiff_pages(file).each do |file| 
              stage cups_converter(file)
            end
          else 
            stage cups_converter(file)
          end
        end

        def stage(file)
          @logger.info "staging file #{ file }"
          dest = @export_dir + file.split('/').last
          `cp #{ file } #{ dest }`
        end

        def combine
          exports = Dir.glob("#{ @export_dir }*.pdf")
          packfile = "#{ @export_dir }/#{ @subdir }-packed.pdf"
          command = "pdfcombine #{ exports.join(" ") } -o #{ packfile }"

          # pack it up!
          `#{ command }`

          packfile
        end

        def cups_converter(file)
          command = "lp -d #{ CUPS_PRINTER } #{ file }"
          regexp = Regexp.new("Auftrags-ID ist #{ CUPS_PRINTER }-(\d*) .*")
          string, id =  *`#{ command }`.match(regexp)
          raise Exception.new("cups printing failed for #{ file }: #{ string }") unless id

          # give the thing a moment to generate
          sleep 1 while !(pdf = Dir.glob("#{ CUPS_ROOT }/job_#{ id }-*.pdf").first)

          pdf
        end

        def extract_tiff_pages(file)
          (1..tiff_page_number(file)).map do |page|
            extract_tiff_page(file, page - 1)
          end
        end

        def extract_tiff_page(file, page)
          filename = "#{ @export_dir }#{ page }-#{ file.split('/').last }"

          @logger.info "tiff: #{ filename }"
          `tiffutil -extract #{ page } #{ file } -out #{ filename }`

          filename
        end

        def tiff_page_number(file)
          `tiffutil -info #{ file } | grep "Directory at" | wc -l`.strip.to_i
        end

        def type(file); file.split(".").last; end
    end

    printer = ReceiptsPrinter.new(month = ARGV.first)
    puts "&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; Please back the files in #{ month } before doing this. Starting; press CTL-C to abort. \n\n"
    printer.print
&lt;/code&gt;&lt;/pre&gt;&lt;img src="http://feeds.feedburner.com/~r/play/type/~4/FdEEItqrxIk" height="1" width="1"/&gt;</description>
      <category domain="http://playtype.net/past/tags/bash">bash</category>
      <category domain="http://playtype.net/past/tags/ruby">ruby</category>
    <feedburner:origLink>http://playtype.net/past/2008/10/23/ruby_and_bash_ftw/</feedburner:origLink></item>
    <item>
      <title>Kick-ass Queuing for Ruby: using RabbitMQ over amqp</title>
      <link>http://feedproxy.google.com/~r/play/type/~3/hkfBoWAhCLs/</link>
      <pubDate>Fri, 10 Oct 2008 16:28:00 GMT</pubDate>
      <guid isPermaLink="false">http://playtype.net/past/2008/10/10/kickass_queuing_over_ruby_using_amqp/</guid>
      <author>rany@playtype.net (rany)</author>
      <description>&lt;p&gt;First off, you&amp;#8217;ll need to install RabbitMQ. &lt;a href="http://playtype.net/past/2008/10/9/installing_rabbitmq_on_osx/"&gt;Instructions for OSX here&lt;/a&gt;. Once you&amp;#8217;ve done that, install the pure ruby amqp library:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    gem sources -a http://gems.github.com
    gem install tmm1-amqp
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You&amp;#8217;re good to go. Now open up two IRB sessions. Paste the following code into the first session: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    require 'mq'

    EM.run {
      amq = MQ.new
      EM.add_periodic_timer(1) { amq.queue("noises").publish("moo") }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Your publishing code has to run inside of an Event Machine loop. You can start it using &lt;code&gt;EM.run&lt;/code&gt;. If you&amp;#8217;re running inside of an Evented Container such as Thin or Evented Mongrel, you can skip this. The meat of it is just &lt;code&gt;amq.queue("noises").publish("moo")&lt;/code&gt;that. &lt;/p&gt;

&lt;p&gt;Now open a second irb session in another terminal tab, and paste this in:         &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    require 'mq'

    EM.run {
      amq = MQ.new
      amq.queue("noises").subscribe { |noise|
        puts noise
      }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You&amp;#8217;ll get the moos off the Queue &amp;#8220;noises&amp;#8221;. No polling required, which is very nice, you simply register a callback with amqp, and it&amp;#8217;ll be called with the message when one becomes available. &lt;/p&gt;

&lt;p&gt;Niceness!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/play/type/~4/hkfBoWAhCLs" height="1" width="1"/&gt;</description>
      <category domain="http://playtype.net/past/tags/amqp">amqp</category>
      <category domain="http://playtype.net/past/tags/asynchronous">asynchronous</category>
      <category domain="http://playtype.net/past/tags/background">background</category>
      <category domain="http://playtype.net/past/tags/queue">queue</category>
      <category domain="http://playtype.net/past/tags/queuing">queuing</category>
      <category domain="http://playtype.net/past/tags/rabbitmq">rabbitmq</category>
      <category domain="http://playtype.net/past/tags/rails">rails</category>
      <category domain="http://playtype.net/past/tags/ruby">ruby</category>
      <category domain="http://playtype.net/past/tags/workling">workling</category>
    <feedburner:origLink>http://playtype.net/past/2008/10/10/kickass_queuing_over_ruby_using_amqp/</feedburner:origLink></item>
    <item>
      <title>Installing RabbitMQ on OSX</title>
      <link>http://feedproxy.google.com/~r/play/type/~3/M_Q1JCQfpVw/</link>
      <pubDate>Thu, 09 Oct 2008 15:36:00 GMT</pubDate>
      <guid isPermaLink="false">http://playtype.net/past/2008/10/9/installing_rabbitmq_on_osx/</guid>
      <author>rany@playtype.net (rany)</author>
      <description>&lt;p&gt;RabbitMQ is a reliable, high performance queue Server written in Erlang. &lt;/p&gt;

&lt;p&gt;Lets fiddle. &lt;/p&gt;

&lt;h2&gt;Before you start&lt;/h2&gt;

&lt;p&gt;Before you can start, you have to have installed Macports. Just in case, here&amp;#8217;s how it&amp;#8217;s done: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Register at the Apple Developers Connection. Go to downloads/developer tools, and download the latest version of the XCode Developer tools. Grab a Flask of coffee.&lt;/li&gt;
&lt;li&gt;Download and run the latest macports installer http://svn.macports.org/repository/macports/downloads/. Grab another Flask. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Installing Erlang&lt;/h2&gt;

&lt;p&gt;If you&amp;#8217;re on the 10.5.3 update, you have to edit the erlang portsfile to avoid a &lt;a href="http://syntatic.wordpress.com/2008/06/12/macports-erlang-bus-error-due-to-mac-os-x-1053-update/"&gt;bus error&lt;/a&gt; bug: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    sudo vi /opt/local/var/macports/sources/rsync.macports.org/release/ports/lang/erlang/Portfile
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Delete this line from configure.args attribute:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    --enable-hipe \
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now you can use port to install erlang: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    port install erlang
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;this is going to take a while.&lt;/p&gt;

&lt;h2&gt;Installing RabbitMQ&lt;/h2&gt;

&lt;p&gt;Grab and unpack the lastest generic Unix version of RabbitMQ. The current version as of writing is 1.4.0. &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    mkdir /tmp/rabbit-mq &amp;amp;&amp;amp; cd /tmp/rabbit-mq
    wget http://www.rabbitmq.com/releases/rabbitmq-server/v1.4.0/rabbitmq-server-generic-unix-1.4.0.tar.gz
    tar xvfz rabbitmq-server-generic-unix-1.4.0.tar.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now move this stuff into erlang&amp;#8217;s magical mystery directory:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    sudo mv rabbitmq_server-1.4.0 /opt/local/lib/erlang/lib
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You&amp;#8217;re good to go! Start up like this: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    sudo /opt/local/lib/erlang/lib/rabbitmq_server-1.4.0/sbin/rabbitmq-server
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Change permissions if you don&amp;#8217;t want to run this as root. &lt;/p&gt;

&lt;p&gt;UPDATE: I&amp;#8217;ve been having troubes starting the server up, since the tables in the mnesia database backing rabbitmq are locked. I don&amp;#8217;t know why this is the case. You can get this running again brute force styleee by deleting the database: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    sudo rm -rf /var/lib/rabbitmq/mnesia
&lt;/code&gt;&lt;/pre&gt;&lt;img src="http://feeds.feedburner.com/~r/play/type/~4/M_Q1JCQfpVw" height="1" width="1"/&gt;</description>
      <category domain="http://playtype.net/past/tags/asynchronous">asynchronous</category>
      <category domain="http://playtype.net/past/tags/background">background</category>
      <category domain="http://playtype.net/past/tags/backgroundjobs">backgroundjobs</category>
      <category domain="http://playtype.net/past/tags/erlang">erlang</category>
      <category domain="http://playtype.net/past/tags/queue">queue</category>
      <category domain="http://playtype.net/past/tags/rabbitmq">rabbitmq</category>
      <category domain="http://playtype.net/past/tags/server">server</category>
      <category domain="http://playtype.net/past/tags/workling">workling</category>
    <feedburner:origLink>http://playtype.net/past/2008/10/9/installing_rabbitmq_on_osx/</feedburner:origLink></item>
    <item>
      <title>Workling Version 0.3 Released</title>
      <link>http://feedproxy.google.com/~r/play/type/~3/mL5DdMIL0Ik/</link>
      <pubDate>Thu, 02 Oct 2008 22:11:00 GMT</pubDate>
      <guid isPermaLink="false">http://playtype.net/past/2008/10/2/workling_version_03_released/</guid>
      <author>rany@playtype.net (rany)</author>
      <description>&lt;p&gt;Workling 0.3 is up on GitHub and svn.playtype.net. This release improves error logging and includes an updated README - finally ;). Here it is&amp;#8230;.&lt;/p&gt;

&lt;h2&gt;Workling&lt;/h2&gt;

&lt;p&gt;Workling gives your Rails App a simple API that you can use to make code run in the background, outside of the your request. &lt;/p&gt;

&lt;p&gt;You can configure how the background code will be run. Currently, workling supports Starling, BackgroundJob and Spawn Runners. Workling is a bit like Actve* for background work: you can write your code once, then swap in any of the supported background Runners later. This keeps things flexible. &lt;/p&gt;

&lt;h3&gt;Installing Workling&lt;/h3&gt;

&lt;p&gt;The easiest way of getting started with workling is like this: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;script/plugin install git://github.com/purzelrakete/workling.git
script/plugin install git://github.com/tra/spawn.git
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you&amp;#8217;re on an older Rails version, there&amp;#8217;s also a subversion mirror wor workling (I&amp;#8217;ll do my best to keep it synched) at:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;script/plugin install http://svn.playtype.net/plugins/workling/
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Writing and calling Workers&lt;/h3&gt;

&lt;p&gt;This is pretty easy. Just put &lt;code&gt;cow_worker.rb&lt;/code&gt; into into &lt;code&gt;app/workers&lt;/code&gt;, and subclass &lt;code&gt;Workling::Base&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# handle asynchronous mooing.
class CowWorker &amp;lt; Workling::Base 
  def moo(options)
    cow = Cow.find(options[:id])
    logger.info("about to moo.")
    cow.moo
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Make sure you have exactly one hash parameter in your methods, workling passes the job :uid into here. Btw, in case you want to follow along with the Mooing, grab &amp;#8216;cows-not-kittens&amp;#8217; off github, it&amp;#8217;s an example workling project. Look at the branches, there&amp;#8217;s one for each Runner.&lt;/p&gt;

&lt;p&gt;Next, you&amp;#8217;ll want to call your workling in a controller. Your controller might looks like this: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class CowsController &amp;lt; ApplicationController

  # milking has the side effect of causing
  # the cow to moo. we don't want to
  # wait for this while milking, though,
  # it would be a terrible waste ouf our time.
  def milk
    @cow = Cow.find(params[:id])
    CowWorker.asynch_moo(:id =&amp;gt; @cow.id)
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Notice the &lt;code&gt;asynch_moo&lt;/code&gt; call to &lt;code&gt;CowWorker&lt;/code&gt;. This will call the &lt;code&gt;moo&lt;/code&gt; method on the &lt;code&gt;CowWorker&lt;/code&gt; in the background, passing any parameters you like on. In fact, workling will call whatever comes after asynch_ as a method on the worker instance. &lt;/p&gt;

&lt;h3&gt;Worker Lifecycle&lt;/h3&gt;

&lt;p&gt;All worker classes must inherit from this class, and be saved in &lt;code&gt;app/workers&lt;/code&gt;. The Worker is loaded once, at which point the instance method &lt;code&gt;create&lt;/code&gt; is called. &lt;/p&gt;

&lt;p&gt;Calling &lt;code&gt;async_my_method&lt;/code&gt; on the worker class will trigger background work. This means that the loaded Worker instance will receive a call to the method &lt;code&gt;my_method(:uid =&amp;gt; "thisjobsuid2348732947923")&lt;/code&gt;. &lt;/p&gt;

&lt;h3&gt;Exception handling in Workers&lt;/h3&gt;

&lt;p&gt;If an exception is raised in your Worker, it will not be propagated to the calling code by workling. This is because the code is called asynchronously, meaning that exceptions may be raised after the calling code has already returned. If you need your calling code to handle exceptional situations, you have to pass the error into the return store. &lt;/p&gt;

&lt;p&gt;Workling does log all exceptions that propagate out of the worker methods. &lt;/p&gt;

&lt;h3&gt;Logging with Workling&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;RAILS_DEFAULT_LOGGER&lt;/code&gt; is available in all workers. Workers also have a logger method which returns the default logger, so you can log like this: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;logger.info("about to moo.")
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;What should I know about the Spawn Runner?&lt;/h3&gt;

&lt;p&gt;Workling automatically detects and uses Spawn, if installed. Spawn basically forks Rails every time you invoke a workling. To see what sort of characteristics this has, go into script/console, and run this: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt; fork { sleep 100 } 
=&amp;gt; 1060 (the pid is returned)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You&amp;#8217;ll see that this executes pretty much instantly. Run &amp;#8216;top&amp;#8217; in another terminal window, and look for the new ruby process. This might be around 30 MB. This tells you that using spawn as a runner will result low latency, but will take at least 30MB for each request you make. &lt;/p&gt;

&lt;p&gt;You cannot run your workers on a remote machine or cluster them with spawn. You also have no persistence: if you&amp;#8217;ve fired of a lot of work and everything dies, there&amp;#8217;s no way of picking up where you left off. &lt;/p&gt;

&lt;h1&gt;Using the Starling runner&lt;/h1&gt;

&lt;p&gt;If you want cross machine jobs with low latency and a low memory overhead, you might want to look into using the Starling Runner. &lt;/p&gt;

&lt;h3&gt;Installing Starling&lt;/h3&gt;

&lt;p&gt;As of 27. September 2008, the recommended Starling setup is as follows:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;gem sources -a http://gems.github.com/ 
sudo gem install starling-starling 
mkdir /var/spool/starling
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The robot Co-Op Memcached Gem version 1.5.0 has several bugs, which have been fixed in the fiveruns-memcache-client gem. The starling-starling gem will install this as a dependency. Refer to the fiveruns README to see what the exact fixes are. &lt;/p&gt;

&lt;p&gt;The Rubyforge Starling gem is also out of date. Currently, the most authorative Project is starling-starling on github (27. September 2008). &lt;/p&gt;

&lt;p&gt;Workling will now automatically detect and use Starling, unless you have also installed Spawn. If you have Spawn installed, you need to tell Workling to use Starling by putting this in your environment.rb: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Workling::Remote.dispatcher = Workling::Remote::Runners::StarlingRunner.new
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Starting up the required processes&lt;/h3&gt;

&lt;p&gt;Here&amp;#8217;s what you need to get up and started in development mode. Look in config/workling.yml to see what the default ports are for other environments. &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sudo starling -d -p 22122
script/workling_client start
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Configuring workling.yml&lt;/h3&gt;

&lt;p&gt;Workling copies a file called workling.yml into your applications config directory. You can delete this file if you&amp;#8217;re not planning to use Starling. The config file tells Workling on which port Starling is listening. &lt;/p&gt;

&lt;p&gt;Notice that the default production port is 15151. This means you&amp;#8217;ll need to start Starling with -p 15151 on production. &lt;/p&gt;

&lt;p&gt;You can also use this config file to pass configuration options to the memcache client which workling uses to connect to starling. use the key &amp;#8216;memcache_options&amp;#8217; for this. &lt;/p&gt;

&lt;p&gt;You can also set sleep time for each Worker. See the key &amp;#8216;listeners&amp;#8217; for this. Put in the modularized Class name as a key. &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;development:
  listens_on: localhost:22122
  sleep_time: 2
  reset_time: 30
  listeners:
    Util:
      sleep_time: 20
  memcache_options:
    namespace: myapp_development

production:
  listens_on: localhost:22122, localhost:221223, localhost:221224
  sleep_time: 2
  reset_time: 30
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note that you can cluster Starling instances by passing a comma separated list of values to &lt;/p&gt;

&lt;p&gt;Sleep time determines the wait time between polls against polls. A single poll will do one .get on every queue (there is a corresponding queue for each worker method).&lt;/p&gt;

&lt;p&gt;If there is a memcache error, the Poller will hang for a bit to give it a chance to fire up again and reset the connection. The wait time can be set with the key reset_time.&lt;/p&gt;

&lt;h3&gt;Seeing what Starling is doing&lt;/h3&gt;

&lt;p&gt;Starling comes with it&amp;#8217;s own script, starling_top. If you want statistics specific to workling, run:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;script/starling_status.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;A Quick Starling Primer&lt;/h3&gt;

&lt;p&gt;You might wonder what exactly starling does. Here&amp;#8217;s a little snippet you can play with to illustrate how it works: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt; 4 # Put messages onto a queue:
 5 require 'memcache'
 6 starling = MemCache.new('localhost:22122')
 7 starling.set('my_queue', 1)
 8 
 9 # Get messages from the queue:
10 require 'memcache'
11 starling = MemCache.new('localhost:22122')
12 loop { puts starling.get('my_queue') }
13
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Using RudeQueue&lt;/h2&gt;

&lt;p&gt;RudeQueue is a Starling-like Queue that runs on top of your database and requires no extra processes. Use this if you don&amp;#8217;t need very fast job processing and want to avoid managing the extra process starling requires.&lt;/p&gt;

&lt;p&gt;Install the RudeQ plugin like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;1 ./script/plugin install git://github.com/matthewrudy/rudeq.git
2 rake queue:setup
3 rake db:migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Configure Workling to use RudeQ. Add this to your environment:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Workling::Clients::MemcacheQueue.memcache_client_class = RudeQ::Client
Workling::Remote.dispatcher = Workling::Remote::Runners::StarlingRunner.new
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now start the Workling Client: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;1 ./script/workling_client start
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You&amp;#8217;re good.&lt;/p&gt;

&lt;h2&gt;Using BackgroundJob&lt;/h2&gt;

&lt;p&gt;If you don&amp;#8217;t want to bother with seperate processes, are not worried about latence or memory footprint, then you might want to use Bj to power workling. &lt;/p&gt;

&lt;p&gt;Install the Bj plugin like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;1 ./script/plugin install http://codeforpeople.rubyforge.org/svn/rails/plugins/bj
2 ./script/bj setup
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Workling will now automatically detect and use Bj, unless you have also installed Starling. If you have Starling installed, you need to tell Workling to use Bj by putting this in your environment.rb: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Workling::Remote.dispatcher = Workling::Remote::Runners::BackgroundjobRunner.new
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Progress indicators and return stores&lt;/h2&gt;

&lt;p&gt;Your worklings can write back to a return store. This allows you to write progress indicators, or access results from your workling. As above, this is fairly slim. Again, you can swap in any return store implementation you like without changing your code. They all behave like memcached. For tests, there is a memory return store, for production use there is currently a starling return store. You can easily add a new return store (over the database for instance) by subclassing &lt;code&gt;Workling::Return::Store::Base&lt;/code&gt;. Configure it like this in your test environment:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Workling::Return::Store.instance = Workling::Return::Store::MemoryReturnStore.new
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Setting and getting values works as follows. Read the next paragraph to see where the job-id comes from. &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Workling.return.set("job-id-1", "moo")
Workling.return.get("job-id-1")           =&amp;gt; "moo"
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here is an example worker that crawls an addressbook and puts results into a return store. Workling makes sure you have a :uid in your argument hash - set the value into the return store using this uid as a key:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;require 'blackbook'
class NetworkWorker &amp;lt; Workling::Base
  def search(options)
    results = Blackbook.get(options[:key], options[:username], options[:password])
    Workling.return.set(options[:uid], results)
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;call your workling as above: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@uid = NetworkWorker.asynch_search(:key =&amp;gt; :gmail, :username =&amp;gt; "foo@gmail.com", :password =&amp;gt; "bar")
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;you can now use the @uid to query the return store:   &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;results = Workling.return.get(@uid)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;of course, you can use this for progress indicators. just put the progress into the return store. &lt;/p&gt;

&lt;p&gt;enjoy!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/play/type/~4/mL5DdMIL0Ik" height="1" width="1"/&gt;</description>
      <category domain="http://playtype.net/past/tags/background">background</category>
      <category domain="http://playtype.net/past/tags/backgroundjobs">backgroundjobs</category>
      <category domain="http://playtype.net/past/tags/rails">rails</category>
      <category domain="http://playtype.net/past/tags/starling">starling</category>
      <category domain="http://playtype.net/past/tags/workling">workling</category>
    <feedburner:origLink>http://playtype.net/past/2008/10/2/workling_version_03_released/</feedburner:origLink></item>
  </channel>
</rss>
