<?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>Ruby Pond</title>
    <link>http://rubypond.com/rss/</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>Ruby Pond Ltd. | Ruby on Rails Developers, Online Marketing, Social Networking</description>
    
    
        <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/rubypond" type="application/rss+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><item>
          <title>JRuby, and why bouncers are assholes</title>
          <description>&lt;p&gt;I&amp;#8217;m normally not one to use this site as a soap box or an outlet for opinion pieces. But I figured it&amp;#8217;s easier than being continually baited into replying on all the various blogs/forums/groups out there. There&amp;#8217;s a whole heap of talk in the ruby community at the moment on JRuby and it&amp;#8217;s place in the ecosystem. Most of it has been kicked off in response to Charles Nutter&amp;#8217;s post on &lt;a href="http://blog.headius.com/2009/07/jrubys-importance-to-ruby-and-erubycon.html"&gt;JRuby&amp;#8217;s importance to Ruby&lt;/a&gt;. I was fortunate enough to see Charles&amp;#8217; presentation at Rails Underground last week and have a chat to him, and he had some great insights and examples of why JRuby actually is pretty damn awesome.&lt;/p&gt;

&lt;h2&gt;The sky is falling&lt;/h2&gt;

&lt;p&gt;The only real problem I have with Charles&amp;#8217; post is the inference that all is lost for the Ruby community if we don&amp;#8217;t embrace JRuby quickly. It&amp;#8217;s not, it won&amp;#8217;t suddenly disappear. What we will possibly lose is the migration of great java developers like &lt;a href="http://twitter.com/jimweirich"&gt;Jim Weirich&lt;/a&gt; and Charles. I&amp;#8217;m willing to excuse a bit of sensationalism in his post though, we&amp;#8217;re effectively talking about his livelihood here. He needs to get people to pay attention, he needs people to use JRuby, he needs more of us to embrace it.&lt;/p&gt;

&lt;p&gt;Whatever his motivations, he&amp;#8217;s right.&lt;/p&gt;

&lt;h2&gt;Java sucks&lt;/h2&gt;

&lt;p&gt;Yes, we know. But you&amp;#8217;ve missed the point. This isn&amp;#8217;t about giving &lt;em&gt;us* java, it&amp;#8217;s about giving *java&lt;/em&gt; ruby. Or better yet, giving ruby the JVM. Just try and think through the argument logically; why on earth would a java developer who is unhappy with java for whatever reason move to a new language, that gave you all the same obstacles? They wouldn&amp;#8217;t (no matter what your opinion of the average java dev is). It&amp;#8217;s about combining the flexibility of ruby with the good bits of java, the bits that are good enough to make people want to stick with java.&lt;/p&gt;

&lt;h2&gt;Java/The JVM is slow&lt;/h2&gt;

&lt;p&gt;&amp;#8220;Good morning Mr. Kettle, I&amp;#8217;m the pot&amp;#8230; and you my friend look black&amp;#8221;. Really? People that are happy enough with the performance of ruby 1.8 are pointing the &amp;#8220;unperformant&amp;#8221; stick at other interpreters? Give me a break. The fact is that the JRuby implementation (in most cases) is as good as 1.8, and in many is as good as 1.9. The fact that such a minor section of the community seems to be aggressively migrating to 1.9 highlights the fact that performance really isn&amp;#8217;t such a high concern for you in real life work, it&amp;#8217;s just a great scape goat for debates. Oh, and worried about that additional few secs startup overhead from starting the JVM for little scripts? Then &lt;a href="http://blog.headius.com/2009/05/jruby-nailgun-support-in-130.html"&gt;don&amp;#8217;t start it up&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;Ruby/rails is a competitive advantage&lt;/h2&gt;

&lt;p&gt;Bull. Shit. Your competitive advantage is your expertise and the ability to meet your client requirements, as efficiently as possible. Period. Ruby and Rails have helped you have an edge for the past few years, but there&amp;#8217;s many alternatives that are closing the gap. Most of my clients don&amp;#8217;t care what technology I use so long as I do the best job for them and make the most efficient use of their money. And why should they care? The few clients I have worked for that would care, would probably still prefer I use java over ruby. &lt;/p&gt;

&lt;h2&gt;The EngineYard deal is acknowledgement from Sun that even they think JRuby is dead&lt;/h2&gt;

&lt;p&gt;Wrong. JRuby is never going to be a core part of Sun&amp;#8217;s business. There are so many people that are happy with the status quo that they will continue coding away in java until such a day it might be declared deceased. Why would they have any desire to invest both time and money to stop such a minor outflow from their business? They wouldn&amp;#8217;t, they&amp;#8217;ve got bigger problems to deal with. On the other hand, EngineYard is all about infrastructure&amp;#8230; and infrastructure for hosting ruby apps specifically. Higher performance ruby implementations means they need to invest less in hardware for the same return. It has a direct and appreciable impact on their bottom line. Sun was a great fit initially to allow the guys access to the java team so we could get the most out of the JVM. EngineYard have more to gain out of a successful JRuby at this point than Sun do, so they&amp;#8217;re a much better fit.&lt;/p&gt;

&lt;h2&gt;We don&amp;#8217;t want more java developers or the java community&lt;/h2&gt;

&lt;p&gt;This is the one that irks me the most. I can only recall two occasions in life where one group of people proclaims they are too cool to associate with another group of people. Bouncers at nightclubs, and &lt;em&gt;&amp;#8220;that&amp;#8221;&lt;/em&gt; group in high school. Bouncers are assholes, and those kids from high school probably grew up to become bouncers. &lt;/p&gt;

&lt;p&gt;If you&amp;#8217;ve got a problem with someone else, that&amp;#8217;s your own insecurities and your problem to deal with, don&amp;#8217;t infect the rest of us with it. What&amp;#8217;s the issue here? You don&amp;#8217;t like their code? Then fork it, fix it, or do it yourself and the problem magically goes away. If you don&amp;#8217;t want to do that, then shut your pie hole and pay some respect to those that actually do the work. You don&amp;#8217;t like the competition? Go read a book or five, find some better developers to pair with, stop trying to hide.&lt;/p&gt;

&lt;p&gt;If we&amp;#8217;re using a technology that really is as great as we make out, we should see it as inevitable that the community is going to grow significantly in size at some point. I think Malcolm Gladwell refers to it as the tipping point. The technical ability of the average developer may drop a little. Commercial interests may infest us and move conferences to Vegas. But for every 10 hacks you still end up with a Jim Weirich, Giles Bowkett, David A. Black, etc. And for every large conference you end up with a Rails Underground, Scotland on Rails, or Ruby Hoedown.&lt;/p&gt;

&lt;h2&gt;Looking at the world through ruby tinted glasses&lt;/h2&gt;

&lt;p&gt;The fact is that for a long time the performance of ruby has sucked. We&amp;#8217;ve worked around it and justified it for a long time, and for the vast majority of people it didn&amp;#8217;t suck bad enough to cause problems. Heck, we were all happy using webrick until Zed Shaw came along and showed us a better way. But 3rd party library support has also sucked. Trying to create PDFs was a nightmare for ages. Trying to work with Microsoft document formats still is. I can think of at least 5 occasions where I&amp;#8217;ve solved a simple problem in another language because doing it in ruby would have been far too difficult.&lt;/p&gt;

&lt;p&gt;For all the talent the community has, there are so many areas where good support is still lacking that it&amp;#8217;s unrealistic to think there are enough talented people who care &lt;em&gt;and&lt;/em&gt; have the time to do anything about it. Combine that with the fact java has at least (commercially) a 10 year head start and the gap between the two languages is huge. Java has plenty to offer us.&lt;/p&gt;

&lt;h2&gt;Why can&amp;#8217;t it all just be peace, love, rainbows, and freakin flying ponies?&lt;/h2&gt;

&lt;p&gt;All this arguing seems to do is instill a whole &amp;#8220;us and them&amp;#8221; mentality. There&amp;#8217;s no need for it to be that way. Embrace JRuby, take advantage of the performance of the JVM, get access to a whole swathe of new libraries that go far beyond what your current list of gems currently offers you&amp;#8230; and get to keep using the language you love.&lt;/p&gt;

&lt;p&gt;One of the things I admire most about the ruby community is the ability it has to support innovations and allow the proverbial cream to rise to the top. WebBrick was replaced by Mongrel, then we had Thin, and now we&amp;#8217;ve got Passenger. SOAP became REST. AttachmentFu became Paperclipped, etc. I&amp;#8217;d hate if in this instance it wasn&amp;#8217;t allowed to happen because of a whole heap of misconceptions about what JRuby was and what it had to offer.&lt;/p&gt;

&lt;p&gt;Or maybe we just need to get a &lt;a href="http://www.mylittledjango.com/"&gt;mascot like Django&lt;/a&gt; and chill the fuck out.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/rubypond/~4/pBRjdwogs3I" height="1" width="1"/&gt;</description>
          <pubDate>Thu, 30 Jul 2009 12:58:30 GMT</pubDate>
          <guid isPermaLink="false">http://rubypond.com/articles/2009/07/30/jruby-and-why-bouncers-are-assholes/</guid>
          <link>http://feedproxy.google.com/~r/rubypond/~3/pBRjdwogs3I/</link>
        <feedburner:origLink>http://rubypond.com/articles/2009/07/30/jruby-and-why-bouncers-are-assholes/</feedburner:origLink></item>
    
        <item>
          <title>Rails Underground - Day 2</title>
          <description>&lt;p&gt;&lt;strong&gt;update:&lt;/strong&gt; &lt;a href="http://www.rails-underground.com/09-conference-schedule.html"&gt;videos are now online&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I was a little late to the kick-off of the keynote on day 2 with Yehuda. For the most part if is another discussion on what&amp;#8217;s happening in Rails 3 so I don&amp;#8217;t think I&amp;#8217;ve missed anything I hadn&amp;#8217;t heard before. He then started to go into more detail on how things have changed than he has previously, probably due to the progress since I last saw him in March&amp;#8230;&lt;/p&gt;

&lt;h2&gt;Yehuda Katz - Keynote&lt;/h2&gt;

&lt;p&gt;Yehuda went through the upcoming changes in approach and philosophy on rails Dev. Having well defined contracts, consistent conventions, and allowing things be be changeable. &lt;/p&gt;

&lt;p&gt;One of the changes of not is splitting off ideas and interfaces into modules which can then be imported into a class at runtime. He used the analogy that your parents didn&amp;#8217;t define everything you could do when you were born, you can learn new things. So to when you define ta class you don&amp;#8217;t have to define the complete interface up front, nor if you want to make changes to you need to re-implement the entire class. Instead you can have sub-sections of interfaces included or changed at runtime where appropriate.&lt;/p&gt;

&lt;p&gt;In Rails 3 this translates into better separation of the components and clearly defined interfaces. No longer are you forced to use ActionView, you just need to use something that is &amp;#8220;ActionView compliant&amp;#8221;. The same goes for ActiveRecord, your models are just &amp;#8220;ActionModel compliant&amp;#8221;.&lt;/p&gt;

&lt;p&gt;An ActionView compliant interface just needs to implement the following 3 methods:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;for_controller(paths, assigns, controller)
render_template(opts)
render_partial(opts)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;An ActiveModel compliant interface needs the following 4 methods:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;new_record?
valid?
errors
model_name&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That last &lt;em&gt;model_name&lt;/em&gt; method needs to return plural, partial name, and a few other things rails expects internally. It&amp;#8217;ll only be 10-20 lines of code to implement manually, or there is a module to include if you just want the default behaviour.&lt;/p&gt;

&lt;p&gt;Next up is a cleaner separation of components, one which we got a demo of was splitting out the ActiveModel::Validations. Now you can create your own ActiveModel compliant persistance/ORM layer, and pull in the existing validations for free regardless of where it&amp;#8217;s stored (YAML, CouchDB, etc.).&lt;/p&gt;

&lt;p&gt;By implementing the above you get a system that fully works with ActionPack, so all the usual form and error helpers will just work.&lt;/p&gt;

&lt;p&gt;The other benefit is that pulling in support for these doesn&amp;#8217;t need you to load the entire Rails stack and rob your machine of RAM, it&amp;#8217;s only a 1.5mb hit.&lt;/p&gt;

&lt;p&gt;Next was the new ActionController::Metal demo. At first I wondered why I&amp;#8217;d bother, it was slightly more vebose than just using Rackable or rolling it manually, and seemed to carry more overhead than Sinatra. But it gives you the ability to opt into additional features easily. If you wall before/after filters you can pull in the callbacks module. If you want better rendering options (various formats, etc) pull in the rendering module, if you want different layouts pull in that module. ActionController::Base is basically just ActionController::Metal with all of the modules pulled in, so you&amp;#8217;ve now got the way to strip back to metal, or anything in between.&lt;/p&gt;

&lt;p&gt;The final piece is ActiveSupport. If for some reason you want to pull in the entire ActiveSupport behemoth you just call:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;require 'active_support/all'&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Otherwise you can call in the various newly separated modules, one of note was &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;require 'active_support/ruby/shim'&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Which backports a bunch of useful ruby1.9 features on datatypes that could be easily implemented in pure ruby. So you can use them now, and when you upgrade to ruby1.9 you get the performance improvement of having them implemented in C.&lt;/p&gt;

&lt;p&gt;The end result is a new level of granularity. Just because you want to use DataMapper doesn&amp;#8217;t mean you have to say goodbye to all of ActiveRecord. So you can opt-out of just the tiny pieces you don&amp;#8217;t want. Conversely, you can start completely bare and opt-in on various features to build your own framework on top of rails. You could now build Sinatra on top of rails, and when the complexity of your app increases you can much more easily pull in the additional features you need. All in all, it sounds really cool. Cool enough that I might even try using it on my next app.&lt;/p&gt;

&lt;h2&gt;Dr. Nic Williams - Dead Simple JavaScript Unit Tests in Rails with Blue Ridge and Screw.Unit&lt;/h2&gt;

&lt;p&gt;Dr. Nic has dropped a very important bombshell right at the start of his talk. The while Australia may indeed be losing the current 5 game series, we are still holders of The Ashes. Now on with the less important aspects of the presentation.&lt;/p&gt;

&lt;p&gt;Nic has a bunch of open source plugins and projects which he could have come and given a presentation on, but he&amp;#8217;s so impressed with BlueRidge that he feels it&amp;#8217;s the most important thing to happen to the Rails ecosystem since Cucumber. At a high level it allows you to do both in browser and headless testing, it fits in with the existing rake tasks, and means that javascript testing should just become part of the usual TDD/BDD development approach and not be an afterthought in Cucumber.&lt;/p&gt;

&lt;p&gt;Generating a javascript test with BlueRidge creates a spec file, and a fixture file which will give you some test HTML. The idea being that most of your javascript ultimately want to modify the DOM, so run the app and copy out the rendered HTML you need to use as your stub.&lt;/p&gt;

&lt;p&gt;An example of a test is as follows:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;require(&amp;quot;spec_helper&amp;quot;);
require(&amp;quot;../../public/javascripts/accounts.js&amp;quot;);

Screw.Unit(function() {
  describe('accounts/new initially has fields', function() {
    it(&amp;quot;should have name field&amp;quot;, function() {
      expect($('#account_name').size()).to(equal, 1);
    });
  });

  describe('accounts/new to fail if missing name', function() {
    it(&amp;quot;should have erroneous name field&amp;quot;, function() {
      $('#account_submit').click();
      expect($('div.fieldWithErrors #account_name').size()).to(equal, 1)
    });
  });
});&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The first test will check that an account_name field exists, and in TDD it shouldn&amp;#8217;t. So next you should go insert the HTML you need into your stub HTML file. (&lt;strong&gt;note:&lt;/strong&gt; you&amp;#8217;ll also need to ensure that the form doesn&amp;#8217;t actually submit on the test, so you need to add a function to return false when the button is clicked).&lt;/p&gt;

&lt;p&gt;When you run the tests you should find that you get nicely green/red tests in browser for any passing/failing tests.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;require(&amp;quot;spec_helper&amp;quot;);
require(&amp;quot;../../public/javascripts/accounts.js&amp;quot;);

Screw.Unit(function() {  
  describe('accounts/new to fail if missing name', function() {
    it(&amp;quot;should have erroneous name field&amp;quot;, function() {
      $('#account_submit').click();
      expect($('div.fieldWithErrors #account_name').size()).to(equal, 1)
    });
  });
});&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Next it is checking that when you submit the form, it wraps the empty form input with the standard rails error div. Run the test, get a failure. Implement the code and the tests go green. You&amp;#8217;ll probably notice that the test passes but you don&amp;#8217;t get the red box, so you need to include the CSS in the test (just below the require) to actually see what the user would:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;css(&amp;quot;../../public/stylesheets/scaffold.js&amp;quot;);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Next Dr. Nic went through a big gotcha, in that that tests don&amp;#8217;t run in transactions. Which means the DOM isn&amp;#8217;t reset back to it&amp;#8217;s an initial state, and he highlighted it by adding an additional test to check that when you input a name that you &lt;em&gt;don&amp;#8217;t&lt;/em&gt; get the error div. Unfortunately the test fails, as the div had been inserted by previous test.&lt;/p&gt;

&lt;p&gt;The way to deal with it was using a before block in your test:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;require(&amp;quot;spec_helper&amp;quot;);
require(&amp;quot;../../public/javascripts/accounts.js&amp;quot;);
css(&amp;quot;../../public/stylesheets/scaffold.js&amp;quot;);

Screw.Unit(function() {  
 before(function() {
   $('#account_name').val('');
   $('div.fieldWithErrors').remove();
 });

 describe('accounts/new to fail if missing name', function() {
   it(&amp;quot;should have erroneous name field&amp;quot;, function() {
     $('#account_submit').click();
     expect($('div.fieldWithErrors #account_name').size()).to(equal, 1)
   });
 });
});&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If someone can come up with an easy way to snapshot the DOM/body.innerHTML and all attached events and substitute it back in after each test that would be awesome.&lt;/p&gt;

&lt;p&gt;Defining your own matches is also pretty simple. You simply add an object with match (to do the comparison) and failure_message to display the feedback and push it onto the matchers hash/dictionary.&lt;/p&gt;

&lt;p&gt;If anybody is wondering if they should test more, you need to watch the last 30secs of the presentation once the videos are available. Great job Nic :)&lt;/p&gt;

&lt;h2&gt;Paolo Negri - Divide and conquer riding rabbits and trading gems&lt;/h2&gt;

&lt;p&gt;Paolo had a problem where he had 1,000,000 search phrases and wanted to compare the results between Google and Bing (warning, don&amp;#8217;t try this at home&amp;#8230; it&amp;#8217;s a breach of the terms of service). Trying to fetch 2m pages is quite a time consuming task, enter a distributed approach.&lt;/p&gt;

&lt;p&gt;How many nodes will you need? How many workers? What mechanism should you use to distribute the work? RabbitMQ is an open source implementation of AMQP, written in Erlang. The fact it&amp;#8217;s written in Erlang is relevant is it inherently supports concurrency, fault tolerance, and distribution of tasks.&lt;/p&gt;

&lt;p&gt;To get started you need to install the rabbitmq package and a gem:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sudo apt-get install rabbitmq
sudo gem install tmm1-amqp&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;There&amp;#8217;s a link to the code at the bottom so rather than transcribe it all I&amp;#8217;ll just talk through it. &lt;/p&gt;

&lt;p&gt;First you create a worker which does the setup and then subscribe to the queue. The queue is where RabbitMQ will store all outstanding work, and the workers will request new work whenever they&amp;#8217;re free for more processing. At the other end of the process what happens is that your app pushes a message to an exchange, the exchange pops it on a queue, and then workers come take it off the queue. You can have multiple exchanges, multiple queues, and multiple workers. Queues and messages are resident in RAM, however you can persist them to disk for further fault tolerance (for a performance hit).&lt;/p&gt;

&lt;p&gt;There are problems though, if a worker has a problem you could lose some messages. To get around it you can send an ACK message from the worker to prefetch a message. You then process the message, and acknowledge completion. Dead client connections go unacknowledged and the work is handed off elsewhere.&lt;/p&gt;

&lt;p&gt;Another problem is there is no easy way to control the workers. The solution to this is to great a system queue. You give each worker a unique ID and explicitly define which exchange you want to use, and make it use the system queue. You then subscribe the worker to that queue. If you want to do something to the worker you simply post the appropriate command onto the system queue, and the worker will then pick up that command as soon as it can. You can also multicast messages to affect all workers.&lt;/p&gt;

&lt;p&gt;Next up was drilling into some of the detail of fetching the pages Google/Bing. For anyone who&amp;#8217;s not done something like this, you really need to use EventMachine and EventMachine::Deferrables. It means you&amp;#8217;re not blocked waiting for socket responses, but can fire callbacks when you get a response back and you can do other tasks (fetch other pages) concurrently in the one process.&lt;/p&gt;

&lt;p&gt;Problem #3 was working out how many workers you had, and keeping track of what they were doing. The solution was somewhat similar to the system approach, setup a heartbeat queue and have the workers post to the queue what they&amp;#8217;re doing. Then create a different worker which subscribes to that queue, takes all the messages off, and posts them somewhere for you to interrogate or view later (like a web page?).&lt;/p&gt;

&lt;p&gt;For doing TDD with RabbitMQ there is &lt;a href="http://github.com/danielsdeleo/moqueue"&gt;moqueue&lt;/a&gt; which gives you easy testing of workers. There is also a web front-end for monitoring your RabbitMQ clients and make sure the system is alive called &lt;a href="http://github.com/auser/alice"&gt;alice&lt;/a&gt;. For testing with EventMachine you&amp;#8217;ve got &lt;a href="http://github.com/tmml/em-spec"&gt;em-spec&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Slides are available at &lt;a href="http://www.slideshare.net/hungryblank/distributed-and-concurrent-programming-with-rabbitmq-and-eventmachine-rails-underground-2009"&gt;SlideShare&lt;/a&gt;.
Code is on &lt;a href="http://github.com/hungryblank/rabbit_starter/tree/master"&gt;Github&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;Pat Allan - &amp;#8220;Sphinx - Beyond the basics&amp;#8221;&lt;/h2&gt;

&lt;p&gt;Sphinx is a full-text search engine, and Thinking Sphinx is an ActiveRecord plugin which will use Sphinx. Once you&amp;#8217;ve set it up if you want to search you just do:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Article.search &amp;quot;my name&amp;quot;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You&amp;#8217;ll also need to use a few rake tasks to create/update the indexes. Now the basics are out of the way.&lt;/p&gt;

&lt;h3&gt;Facets&lt;/h3&gt;

&lt;p&gt;Pat&amp;#8217;s written some ruby code to provide a means of offering facets (search summaries) like other engines like Solr offer. To use you do &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;indexed field, :facet =&amp;gt; true
has_attribute , :fa&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;rebuild your indexes, then you&amp;#8217;re good to search.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@facets = Article.facets 'pancakes'
@facets == { :author =&amp;gt; { &amp;quot;Pat Allan&amp;quot; =&amp;gt; 12,
                         &amp;quot;Glenn Gillen&amp;quot; =&amp;gt; 1}.
            :tags =&amp;gt; { &amp;quot;breakfast&amp;quot; =&amp;gt; 2,
                       &amp;quot;brunch&amp;quot; =&amp;gt; 3}}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can then drill into the results to return just the Articles for a specific author:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@facet.for(:author =&amp;gt; &amp;quot;Pat Allan&amp;quot;)&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Excerpts&lt;/h3&gt;

&lt;p&gt;It got a bit of a cheer, apparently some people have been hanging out for them. They&amp;#8217;re basically the bold bits on a Google page of results, where the actual search terms are highlighted within the results. The syntax is:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@articles = Article.search 'pancakes'
@article.excerpts.body&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Geo-searching&lt;/h3&gt;

&lt;p&gt;Pat thinks this could be the killer feature. The only gotcha is that the datatypes need to be in radians as floats. To define lats and longs on your models just do:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;has latitude
has longitude&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you&amp;#8217;ve already got lat and long data as degrees and decimals you can do:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;has 'RADIANS(latitude)',
    :as =&amp;gt; :latitude,
    :type =&amp;gt; :float
has 'RADIANS(latitude)',
    :as =&amp;gt; :latitude,
    :type =&amp;gt; :float&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once that&amp;#8217;s done, you can find all pubs that serve pancakes near a given point:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@pubs = Pub.search 'pancakes'
  :geo =&amp;gt; [@lat, @lng],
  :order =&amp;gt; '@geodist ASC'&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once you&amp;#8217;ve got your pubs you probably want&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@pubs.each_with_geodist do |pub, distance|
end&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;International Sphinx&lt;/h3&gt;

&lt;p&gt;Using the character set table means you can now handle international characters easier, so an &amp;#8220;e&amp;#8221; with an accent can be treated as a plain old english &amp;#8220;e&amp;#8221;. Same with other similar characters. &lt;/p&gt;

&lt;p&gt;With word stemming you can now define a custom stemmer to help you handle stemming in various languages without too much trouble. &lt;/p&gt;

&lt;h3&gt;Different Delta Approaches&lt;/h3&gt;

&lt;p&gt;In most rails apps you want information to be available as quickly as possible, so when users update a field you don&amp;#8217;t want to have to rebuild the whole index. To get around that you can do the following:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;define_index do
  set_property :delta =&amp;gt; true
end&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The downside is you end up with a new column on the table, and a little additional HTTP overhead.&lt;/p&gt;

&lt;p&gt;Instead you can skip the extra column and use the updated_at field to manage deltas:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;define_index do
  set_property :delta =&amp;gt; :datetime, :threshold =&amp;gt; 2.hours
end&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The downside is they&amp;#8217;re not updated immediately, they&amp;#8217;re updated every 2 hours. Another alternative is to use the delayed option which pushes the request out of process using DelayedJob.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;define_index do
  set_property :delta =&amp;gt; :delayed
end&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Removes indexing from HTTP requests, and the results are available soon(ish). The downside is it needs a constantly running rake task (using delayedjob) and a long queue of updates could take a while to get through.&lt;/p&gt;

&lt;h3&gt;Multi-server deployment&lt;/h3&gt;

&lt;p&gt;Basically, set the remote_spinx value to true, use the DelayedJob approach, and make sure Spinx and the database are on the same server. You&amp;#8217;ll probably want the DelayedJob and ThinkingSphinx on that server too. Pat&amp;#8217;s not actively using multi-server setups so is looking for feedback on this.&lt;/p&gt;

&lt;h3&gt;Sphinx Scopes&lt;/h3&gt;

&lt;p&gt;People have been using named_scopes with Sphinx and it&amp;#8217;s caused some problems because of the difference between SQL and Sphinx. The new sphinx scopes are an implementation that works just like named scopes, except they chain together properly and work with pagination.&lt;/p&gt;

&lt;h2&gt;Brendan Lim - Mobilize Your Rails Application&lt;/h2&gt;

&lt;p&gt;There are an estimated 4 billing mobile users, which means there are more phones than there are personal computers. Approximately half of those users have mobile web access. With that much reach you need to make your apps accessible to as many of these users as possible.&lt;/p&gt;

&lt;p&gt;THe problem with trying to do just one webpage for all devices is problematic because of resolution differences, javascript, flash, bandwidth, etc.&lt;/p&gt;

&lt;p&gt;Enter MobileFu. It&amp;#8217;ll detect if a user is mobile, can add custom styling based on user agent and takes you one step closer to one webpage. You use it like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class MyController &amp;lt; ActionController::Base
  has_mobile_fu

  def index
    respond_to do |format
      format.html
      format.mobile
    end
  end
end&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Which will output the page in a mobile friendly format. Some of the magic happens in the stylessheet_include_tag:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;stylessheet_include_tag &amp;quot;foo&amp;quot;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;will look for foo_iphone.css, foo_android.css, etc. so you can have custom styles for each device. You&amp;#8217;ve also got various helper methods to take actions within you view:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;is_mobile_device?
is_device?('blackberry')
in_mobile_view?&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Rails iUI is a wrapper for the iUI user interface framework. It takes care of detecing rotating an iPhone and various other helpers. To use it you do the following in your controller:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class MyController &amp;lt; ActionController::Base
  acts_as_iphone_controller

  def index
    respond_to do |format|
      format.html
      format.iphone
    end
  end
end&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And then within your view:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;%=include_iui_files%&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To display iPhone styled default interface objects you&amp;#8217;ve the following methods:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;iui_toolbar( initial_caption, search_url)
button_link_to()
iui_list(items, opts)
iui_grouped_list(items, opts, &amp;amp;group_by_block)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Again you need add some stuff into your views to make it work:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;body &amp;lt;%=register_orientation_change%&amp;gt;&amp;gt;
observe_orientation_change(url_for_options)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;you&amp;#8217;ll receive params[:position] in the request to the url you put in observe_orientation_change, 0 is upright. 90 and -90 is the two various landscape modes (depending on rotation).&lt;/p&gt;

&lt;p&gt;Next up is SMS. You can use Clickatell&amp;#8217;s API. Once you connect to the API you just do:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;api.send_message(phone_number, message)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Brendan also wrote something called SMSFu. The problems are that you need to know the recipients carrier and it doesn&amp;#8217;t support as many carriers. But because it sends SMS via email it&amp;#8217;s free to send (to supported carriers). To send you just do:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;deliver_sms(phone_number, carrier, message)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now how do you receive SMS? Well you can get a shortcode (an inbound SMS number like 80800), but they&amp;#8217;re quite expensive to setup with monthly costs associated. Alternatively there are providers (like TextMarks) where you&amp;#8217;re given a keyword that users need to prepend on the front of their message, and you share a shortcode. TextMarks then send the next set of words (up to 9) as an array to you to do what you want with.&lt;/p&gt;

&lt;p&gt;If you&amp;#8217;re wanting to receive MMS, then MMS2R is the gem to use. It strips out any carrier advertising and other stuff that may be added to the core attachment that you actually want. You&amp;#8217;ll get access to the message subject, body, and default_media (the attachment).&lt;/p&gt;

&lt;h2&gt;Jim Weirich, DHH, Obie Fernandez, Geoffrey Grosenbach, Jonathan Siegel - Q&amp;amp;A Session&lt;/h2&gt;

&lt;p&gt;Bah, I can&amp;#8217;t keep pace with questions and responses in a fashion that does them justice. Best wait for the videos.&lt;/p&gt;

&lt;h2&gt;Eleanor McHugh - The Ruby Guide to *nix Plumbing&lt;/h2&gt;

&lt;p&gt;Unix isn&amp;#8217;t just a server based operating system, it&amp;#8217;s a philosophy. It consists of a whole heap of little tools that all receive a little piece of input and do just a little piece of work. And they can all work together to achieve much bigger tasks.&lt;/p&gt;

&lt;p&gt;Eleanor went through a bit of an explanation of how she felt the fascination on better threading in ruby is a bit stupid, and that the introduction of the threading approach we have in 1.8 was a backward step from 1.6. Most problems you can get around by forking the process, and threading is very rarely the appropriate solution to the problem.&lt;/p&gt;

&lt;p&gt;The shell system is simply a process which provides you an interactive environment, job/process management, and a scripting language. However, you can get around using the standard shell scripting languages by using the under-documented shell.rb.&lt;/p&gt;

&lt;p&gt;A little known fact is that the IO/File new methods don&amp;#8217;t just accept a string, but will also accept a file descriptor (which is just a number). So if you open 0, 1, or 2 you&amp;#8217;ll end up writing directly to STDIN, STDOUT, and STDERR respectively.&lt;/p&gt;

&lt;p&gt;Taking the approach further means that you can effectively communicate with any process in a similar fashion by sending signals to the appropriate process descriptors.&lt;/p&gt;

&lt;p&gt;When working with sockets in ruby, Eleanor said you should turn off reverse DNS lookups as they will greatly slow down network connection and tie up your processes needlessly.&lt;/p&gt;

&lt;p&gt;There&amp;#8217;s also a whole host of examples of using low level system calls and examples in the &lt;a href="http://slides.games-with-brains.net/"&gt;slides&lt;/a&gt; which I&amp;#8217;ve no chance of transcribing quickly enough with the right context.&lt;/p&gt;

&lt;h2&gt;Lindsay Holmwood - Behaviour driven monitorins with cucumber-nagios&lt;/h2&gt;

&lt;p&gt;Everyone is probably familiar these days with what Cucumber is and the descriptive syntax it gives you for defining tests. You can combine it with webrat and mechanize to give you a way of monitoring external websites. To get started you need to install the gem and setup a new project:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sudo gem install auxesis-cucumber-nagios
cucumber-nagios-gen project mysites
cd mysites
rake deps&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You then create a new feature, for the sake of example create one to test the navigation:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;cucumber-nagios-gen feature rails-underground.com navigation&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;which will create the file features/rails-underground.com/navigation.feature. From there you can use all the usual cucumber/webrat commands to go to a page, click links, test for the result text. To run the test you:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;cucumber-nagios features/rails-underground.com/navigation.feature&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;cucumber-nagios will then take care of taking the cucumber results and outputting them in a nagios plugin format.&lt;/p&gt;

&lt;p&gt;The caveats are that you&amp;#8217;re going to run problems if you site is javascript dependent. But you&amp;#8217;re embracing progressive enhancement and your site works fine without javascript, right? You could also run into some problems if you&amp;#8217;re trying to run multiple scenarios within the same feature file.&lt;/p&gt;

&lt;p&gt;The big benefits of this approach is that you end up making your system monitoring actually test what you care about. Traditionally these tests are just a ping or socket connection. While if you&amp;#8217;ve got a server down your tests will definitely fail, but there is a whole host of other cases where your site might be responsive but not usable by a real person (DB down?). Lindsay argues that this approach is really (and should be) continuous integration for systems monitoring.&lt;/p&gt;

&lt;p&gt;Some cool examples of people extending it include telephony-systems-test which allows you to test an Asterisk/Adhearsion telephony dial plan. cucumber+dash which pull metrics out of the hosted Dash metrics system to alert you of application performance problems.&lt;/p&gt;

&lt;p&gt;Lindsay went through what seemed like an awesome replacement for nagios that he&amp;#8217;s been working on, which is handling upto 6,000 tests per second. It&amp;#8217;s super top secret at the moment and didn&amp;#8217;t want specifics to go outside the room. He&amp;#8217;s auxesis on github, you join the dots.&lt;/p&gt;

&lt;p&gt;I&amp;#8217;ve also got summaries for &lt;a href="/articles/2009/07/24/rails-underground---day-1/"&gt;Rails Underground - Day 1&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/rubypond/~4/7FmEVqSp8PA" height="1" width="1"/&gt;</description>
          <pubDate>Sat, 25 Jul 2009 08:59:19 GMT</pubDate>
          <guid isPermaLink="false">http://rubypond.com/articles/2009/07/25/rails-underground---day-2/</guid>
          <link>http://feedproxy.google.com/~r/rubypond/~3/7FmEVqSp8PA/</link>
        <feedburner:origLink>http://rubypond.com/articles/2009/07/25/rails-underground---day-2/</feedburner:origLink></item>
    
        <item>
          <title>Rails Underground - Day 1</title>
          <description>&lt;p&gt;&lt;strong&gt;update&lt;/strong&gt; I&amp;#8217;ve also got summaries for &lt;a href="/articles/2009/07/25/rails-underground---day-2/"&gt;Rails Underground - Day 2&lt;/a&gt;, plus &lt;a href="http://www.rails-underground.com/09-conference-schedule.html"&gt;videos are now online&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Today kicks off the first day of talks for Rails Underground in London. Thankfully they&amp;#8217;ve got usable wifi, and two days of what sound like mostly excellent talks. So over the next couple of days I&amp;#8217;ll hopefully be able to post summaries of the various talks I&amp;#8217;m able to squeeze into.&lt;/p&gt;

&lt;p&gt;So first up&amp;#8230; &lt;/p&gt;

&lt;h2&gt;Fred George - Rails is a Hammer (Keynote)&lt;/h2&gt;

&lt;p&gt;Taking a queue from Martin Fowler&amp;#8217;s &amp;#8220;Patterns of Enterprise Application Architectures&amp;#8221; Fred goes in to discuss the various trade offs that happen in different application styles. Essentially, approaches that get you going quickly usually become exponentially more difficult as the complexity of your problem increases. Rails with it&amp;#8217;s &amp;#8220;convention over configuration&amp;#8221; offers a great way to prototype very quickly, but by Fowler&amp;#8217;s assumption this should get slower and slower to extend as your app complexity grows. So Fred decided to start from scratch, take his learnings from smalltalk and see how things would turn out on a new project.&lt;/p&gt;

&lt;p&gt;Models were just pure ruby, storing to a YAML persistance layer. Sinatra took control of the controllers, with HAML/SASS for the view layer. I turns out that with very little code, YAML provided an easy way to persist the objects quickly to disk. Incrementing filenames on each save also meant that object state was versioned, and actions were simple to undo/redo (and note we&amp;#8217;re talking full objects being serialised to disk with fill state, not just instance variables/attributes).&lt;/p&gt;

&lt;p&gt;Then to link the model with the view comes in Sinatra. It&amp;#8217;s so light and easy, does nothing more than it needs to, it&amp;#8217;s essentially just a set of regexps that push a request to the appropriate code. And how much more do you really need to do in most controllers?&lt;/p&gt;

&lt;p&gt;Next was the view, and for anyone using HAML there is nothing groundbreaking to learn here. It&amp;#8217;s awesome, heaps better than the prescribed ERB, and you should be using it (my conclusion on Fred&amp;#8217;s discussion and my own experience, don&amp;#8217;t flame him directly). It&amp;#8217;s not just less typing, the prescriptive structure makes the code more readable and being able to use variables in CSS is incredibly liberating and DRY.&lt;/p&gt;

&lt;p&gt;Some good debate post keynote on whether it was really a comparison of rails vs alternatives, or just a test project using stuff rails already offers you without using rails.&lt;/p&gt;

&lt;h2&gt;Gwyn Morfey - Refactoring with Fire&lt;/h2&gt;

&lt;p&gt;A high paced and amusing presentation about trying to refactor old apps. Why would you need to consider a rewrite? &amp;#8220;Maybe it was PHP on Rails, or some guys first Rails project&amp;#8221;. The options are burn it to the ground and start again, or try and refactor it. The default option should always be to try and refactor. It&amp;#8217;s usually tested, and it works, and if you re-write it you&amp;#8217;re basically shutting down and delivering absolutely nothing for an extended period town. At which point you eventually release, with the same feature set. And even if you&amp;#8217;re re-writing in an agile fashion, your first releases still have only a minor percentage of the features expected by the users and will be considered a fail.&lt;/p&gt;

&lt;p&gt;You refactor unless:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have a very, very, very good reason to rewrite&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You rewrite if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can drastically cut scope&lt;/li&gt;
&lt;li&gt;You won&amp;#8217;t stall the business&lt;/li&gt;
&lt;li&gt;You have access to the original coder&lt;/li&gt;
&lt;li&gt;The existing business logic is fundamentally wrong (the requirements have changed?)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When going through the estimation phase, it&amp;#8217;s often not accurate to just take a cursory glance at the code and see how easy it is to understand. So much can be hidden under the surface that it&amp;#8217;s usually best to try a spike at implementing something new and see how easy it is.&lt;/p&gt;

&lt;p&gt;If you&amp;#8217;re considering a re-write, you need to approach it as a complete outsider and say &amp;#8220;I know nothing, what does this thing need to do&amp;#8221;. Otherwise you open yourself up to the stealth feature that was hidden deep in the code that you lost in your re-write. Boot it up, use the application&amp;#8230; and watch the client use the application.&lt;/p&gt;

&lt;p&gt;To make it work and keep the client happy, make sure that any re-write is actually adding value to the existing system. Deliver early, deliver often.&lt;/p&gt;

&lt;p&gt;That&amp;#8217;s it on re-writing, next is how to make refactoring manageable. &lt;/p&gt;

&lt;p&gt;Gwyn also added in a shameless plug for his &amp;#8220;noisy partials&amp;#8221; plugin which will insert HTML comments in the rendered views to help you identify where exactly things are being rendered. The &amp;#8220;partial dependencies&amp;#8221; plugin will draw a nice pretty dependency graph showing the links between all the partials in a project.&lt;/p&gt;

&lt;p&gt;Split the long methods into smaller workable and meaningful chunks. You can then work on refactoring these bite sized pieces while you&amp;#8217;re working through the codebase. If there&amp;#8217;s code you don&amp;#8217;t understand, delete it and see what breaks. If nothing breaks, kiss it goodbye&amp;#8230; &amp;#8220;Deleted code is debugged code&amp;#8221;. Make sure that you&amp;#8217;re only working on the small bites, and get it back to a working state before you continue. Otherwise you&amp;#8217;ll forget what it was you were originally working on, and before you know it you&amp;#8217;ll have upgraded your entire rails stack and plugins and nothing works. And don&amp;#8217;t refactor code just because you hate it, much sure you hate it &lt;em&gt;and&lt;/em&gt; it&amp;#8217;s in your way.&lt;/p&gt;

&lt;p&gt;Check out Sequel as the database adapter if you&amp;#8217;re trying to move legacy data between SQL database schemas.&lt;/p&gt;

&lt;h2&gt;Desi McAdam - Working with Legacy Rails Apps - Technical Debt Hell and how to work your way out of it&lt;/h2&gt;

&lt;p&gt;Another talk on dealing with old rails projects and code, am I detecting a general theme here? So what constitutes legacy apps here?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Old Rails versions&lt;/li&gt;
&lt;li&gt;No tests&lt;/li&gt;
&lt;li&gt;Complexity in architecture and design&lt;/li&gt;
&lt;li&gt;Violates best practices (fat controllers, poor separation, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Essentially, code that is difficult to change. It all boils down to technical debt. &lt;/p&gt;

&lt;p&gt;At Hash Rocket they have rescue missions which they do for clients they inherit with these problems. First off they&amp;#8217;ll spend a couple of days doing a code audit. As Gwyn pointed out though, much of the detail isn&amp;#8217;t uncovered until you try and implement something.&lt;/p&gt;

&lt;p&gt;So what to do and how to make it workable?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pair all the f&lt;em&gt;*&lt;/em&gt;ing time. You need someone to check you and stop you from being sucked in to writing the same style of bad code. If you look at it long enough you&amp;#8217;ll start to find it normal.&lt;/li&gt;
&lt;li&gt;Be fearless. Take the risks and delete the code.&lt;/li&gt;
&lt;li&gt;Write tests. Use cucumber to wrap the main functionality to help give yourself a sense of stability.&lt;/li&gt;
&lt;li&gt;Stay focussed. At the point of change wrap tests around the current functionality, BDD the changes. And don&amp;#8217;t get distracted (at which point it an amp starts squealing in the background and disrupts the talk&amp;#8230; brilliant timing)&lt;/li&gt;
&lt;li&gt;Refactor. Always refactor with tests, and do it as you go. It helps estimate velocity, removes technical debt, and alleviates the broken windows problem.&lt;/li&gt;
&lt;li&gt;Use Git (or anything that lets you branch easily)&lt;/li&gt;
&lt;li&gt;Use your customer. Deliver early and often, and get them to help you test. They&amp;#8217;ve got the best idea of what it&amp;#8217;s meant to do.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The specific things hash Rocket do in their approach is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Update the app to the latest rails version&lt;/li&gt;
&lt;li&gt;Update the plugins and external dependencies (or remove/un-fork where possible). &lt;/li&gt;
&lt;li&gt;Fix the easy stuff. Clean the formatting, remove comment code as you&amp;#8217;re unlikely to get much value from is.&lt;/li&gt;
&lt;li&gt;Use the free stuff. Replace custom code with built-in rails functionality, use new features (like named scopes) to decrease complexity.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Obie Fernandez - Blood, Sweat, and Rails&lt;/h2&gt;

&lt;p&gt;Obie got introduced to ruby via Aslak of Cucumber fame back in 2004, with rails in 2005 for a client in London. &lt;/p&gt;

&lt;h3&gt;Sales &amp;amp; Marketing&lt;/h3&gt;

&lt;p&gt;First piece of advice, controversy can be useful. One of the first things Obie did was writing the top 10 reasons why Java sucks. Just make sure you can take the abuse, especially if you deserve it&amp;#8230; he&amp;#8217;s willing to admit his initial ideas for Rails Maturity Model sucked. Confidence always win. On the flip side of that, don&amp;#8217;t ever seem desperate. The moment you go in and convey, even subconsciously, that you need the work you make the client wonder why you need the work. They&amp;#8217;d much rather work with someone who is in demand. Referrals will be the lifeblood of your business, so go ahead and videotape your clients. Get them used to it, video meetings, testimonials, and help document the project. It&amp;#8217;s a tactical nuclear weapon.&lt;/p&gt;

&lt;p&gt;Make it personal, pick up the phone and call a potential client. It&amp;#8217;s way too easy to just fire off an email or IM.&lt;/p&gt;

&lt;h3&gt;Legal Matters&lt;/h3&gt;

&lt;p&gt;You need to write bulletproof contracts. Keeping them simple is not ideal, too many potential problems. Master services agreements, warranties, digital signing/acceptance, etc. all need to be included. Don&amp;#8217;t negotiate under pressure. You&amp;#8217;ll end up agreeing to things that you shouldn&amp;#8217;t to and it will cost you more in the long run. And when things go wrong, lawyers cost a lot of money.&lt;/p&gt;

&lt;h3&gt;Finances&lt;/h3&gt;

&lt;p&gt;Don&amp;#8217;t undercapitalise. Don&amp;#8217;t do fixed bid contracts. None of Hash Rocket&amp;#8217;s contracts state a deliverable, you&amp;#8217;re buying time and expertise. There is no definition of scope. By documenting scope you&amp;#8217;re breaking the philosophy of agile and you lose the ability to adjust scope. You&amp;#8217;ll probably need to gather some momentum before these are applicable. Make sure you charge what you are worth (a slide showing that Obie billed out at $250/hr, other devs at $190/hr for short term work $150/hr for anything greater than 6 months). Allow some budget for non-paying clients. When you&amp;#8217;re extending credit, some times they&amp;#8217;re just not going to pay (either because they&amp;#8217;re bastards, they ran out of funding, they&amp;#8217;ve drained the kids college fund, etc.). Allow some budget for product development, everyone wants to be 37signals eventually. Don&amp;#8217;t invoice manually, Harvest is a good example of how to handle it easily.&lt;/p&gt;

&lt;h3&gt;Agile Practices&lt;/h3&gt;

&lt;p&gt;You shouldn&amp;#8217;t have to defend agile. Just start from the assumption that &amp;#8220;this is how we work&amp;#8221; and that it&amp;#8217;s that the normal case, then you&amp;#8217;re not having to justify a change in approach. Always do agile by the book first, don&amp;#8217;t try and tweak it and then wonder why it doesn&amp;#8217;t work. Storycard the work, either on index cards or in something like Pivotal Tracker.&lt;/p&gt;

&lt;h3&gt;Client Relationships&lt;/h3&gt;

&lt;p&gt;Execution is absolutely critical. If you&amp;#8217;re not consistently striving for excellence then it&amp;#8217;s all worthless. Going hand-in-hand with that is perception is reality. If you can&amp;#8217;t relay to the client that you&amp;#8217;re doing an excellent job then it&amp;#8217;s worthless also. Bend the rules, the client just wants a result and doesn&amp;#8217;t really care how you go about it. In some cases you should even break the rules. Another simple one is to establish contact, especially with big value contracts, call them every day. Mind their budget, especially if you&amp;#8217;ve made the jump to time based contracts. The responsibility falls back on to you to make sure you&amp;#8217;re not burning through all their cash so have transparency on who&amp;#8217;s working and what the cash burn rate is. Do what you can to win their people over. Fire clients if they deserve to be fired. Hire people on a contract to perm basis &lt;em&gt;(bah! I say they should all be contractors if they&amp;#8217;re any good)&lt;/em&gt;. Keep your employees constantly learning. And make them pair all the time. Empower your employees for change, be open for them to challenge the generally accepted practices (like pairing all the time). You need to actively work to make that possible. Make the work environment appealing and always have fun. Keep everyone in the loop all the time.&lt;/p&gt;

&lt;h2&gt;Charles Nutter - The Present and Future of JRuby (and the Future of Rails as it relates to JRuby)&lt;/h2&gt;

&lt;p&gt;The current version of JRuby is 1.3.1, it&amp;#8217;s ruby 1.8.6 compatible (give or take, it can&amp;#8217;t do continuations and some other things). Has some ruby 1.9 support (somewhere 75%-90% done). &lt;/p&gt;

&lt;p&gt;It&amp;#8217;s roughly equivalent in performance terms to ruby 1.9, with real native threads and runs rails fine. It takes at least 0.5secs to start up, but can take several seconds so it&amp;#8217;s not great. Once up, it should be faster than 1.9 in almost all cases. Most ruby application bottlenecks are in the core classes and not ruby itself (string manipulations, working with hashes, etc.) and the JRuby performance in these classes is mixed which makes providing meaningful benchmarks almost impossible. Some cases are great, others are bad. Also because of the iterative optimisations that the JVM does internally a single run of an application doesn&amp;#8217;t show real world performance. To demonstrate Charles ran a fractal generation program 5 times and you could see how much quicker the latter runs were.&lt;/p&gt;

&lt;p&gt;It&amp;#8217;s the only ruby implementation with true native threads. Again another demonstration showing how with JRuby you can effectively max out two cores, yet with regular ruby, even 1.9, you only ever really get the equivalent of 1 core working (spread across the two cores). Next was a really cool demo that I&amp;#8217;ll do no justice here. Basically using FFI to call C functions and have them then execute ruby callbacks.&lt;/p&gt;

&lt;p&gt;Next was a demo of starting an app (Typo) using Glassfish. Just download the gem, go into the app and:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;quot;glassfish -e production&amp;quot;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you&amp;#8217;re running threadsafe rails it will start just one instance of the JVM and keep memory usage low. In any event, it takes care of concurrency and all the usual problems for you.&lt;/p&gt;

&lt;h3&gt;Ruby 1.9 support&lt;/h3&gt;

&lt;p&gt;It&amp;#8217;s at about maybe 80% of 1.9.1. The 1.9.2 release adds a bunch more, but they&amp;#8217;d like some help (so get in touch if you&amp;#8217;re interested/able). He also gave a cool example of a change to regexps in 1.9 that I&amp;#8217;d not seen. If you want to extract a grouped match from a regexp pattern you&amp;#8217;d normally do something like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;str = &amp;quot;Welcome to Rails Underground&amp;quot;
matched = str.match(/Welcome to Rails (.*)/)
matched[1] == &amp;quot;Underground&amp;quot;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The problem is if you change the regexp to:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;matched = str.match(/(Welcome) to Rails (.*)/)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;matched[1] now becomes &amp;#8220;Welcome&amp;#8221; rather than underground, and your code brakes. In ruby 1.9 you can name grouped matches:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;str = &amp;quot;Welcome to Rails Underground&amp;quot;
matched = str.match(/Welcome to Rails (?&amp;lt;conf&amp;gt;.*)/)
matched[:conf] == &amp;quot;Underground&amp;quot;&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;What&amp;#8217;s missing?&lt;/h3&gt;

&lt;p&gt;One thing that isn&amp;#8217;t missing is 1.8 compatibility, it&amp;#8217;s a mostly solved problem. There are some edge cases that wont work, but it&amp;#8217;s got the best coverage of all the alternative estimates. On the todo list is better performance, better java integration. To highlight some of the upcoming performance improvements Charles went through the intermediate code the new compiler generates which was quite fascinating to see. Makes me glad I don&amp;#8217;t have to build compilers for a living though. As a result there is also a &amp;#8220;ruby2java&amp;#8221; executable which generates java classes from ruby code now. And to further help with the java and ruby integration there is a &amp;#8220;become_java&amp;#8221; method you can call on a ruby class to turn it into an object that can be used natively within java.&lt;/p&gt;

&lt;p&gt;The other tasks are rubifying the java libraries. Hibernate, Ant, Maven, etc. It&amp;#8217;s basically an attempt to keep all of the rich functionality and performance that java offers, without the inherent java ugliness. The big news is that Hibernate is basically done now and just needs to be wrapped up in a nice ruby DSL. But you can use Hibernate as a persistance layer in JRuby now.&lt;/p&gt;

&lt;h3&gt;How can you help&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use JRuby every chance you get&lt;/li&gt;
&lt;li&gt;Help improve JRuby (especially 1.9)&lt;/li&gt;
&lt;li&gt;Start evangelising ruby at java conferences&lt;/li&gt;
&lt;li&gt;Study Groovy, Scala, Clojure, etc&lt;/li&gt;
&lt;li&gt;Study java libraries and help rubify them&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Gwyn Morfey (filling in for Laurie Young) - Agile Deployment with Ruby&lt;/h2&gt;

&lt;p&gt;You&amp;#8217;re going to need a lot of servers, so you may as well make the job as easy as possible. You&amp;#8217;ve got lost of serving options, you should be using passenger though. There&amp;#8217;s now Ruby Intelligent Packaging called RIP which is great and you should use it, except it doesn&amp;#8217;t work&amp;#8230; so just keep an eye on it. You also need to have continuous integration setup and working. CruiseControl.rb isn&amp;#8217;t great but it works. &lt;/p&gt;

&lt;p&gt;As far as actually deploying, capistrano is there. But use webistrano, it&amp;#8217;s a thin layer over capistrano. The main benefit is you get an audit history so you can see who deployed what, where, and when. Next is puppet to manage your server config. It&amp;#8217;s not as quick is making disk images, but it&amp;#8217;s more flexible and much easier to maintain &lt;em&gt;(Personally, I&amp;#8217;d advocate Chef instead of puppet)&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;You need multiple servers (either actual dedicated iron for each, or virtualised environments) to deploy to. A test server for external testers and/or clients to run through the completed development prior to a production deployment. A pre-production server that closely mirrors the production server. It&amp;#8217;s there as a final sanity check prior to going live, and there to check there isn&amp;#8217;t some configuration difference on the production box that will trip you up. Make sure you test both the up and down migrations, it should work, but it often doesn&amp;#8217;t. And there is nothing worth than discovering the rollback won&amp;#8217;t work once you get to production.&lt;/p&gt;

&lt;p&gt;On production, make sure you&amp;#8217;ve got notifications setup. &amp;#8220;Exception Notifier&amp;#8221;, &amp;#8220;Get Exceptional&amp;#8221;, &amp;#8220;HopToad&amp;#8221;, &amp;#8220;New Relic&amp;#8221; test them all and make a call, just use one. Service monitoring, make sure things stay up with monit or god. To help with peak loads consider a Content Delivery Network like Limelight Networks.&lt;/p&gt;

&lt;p&gt;You&amp;#8217;ll probably need a debug server. If you push things all the way to production and you get errors, it&amp;#8217;s quite possibly data related. You need to copy the logs, assets, and all other data over to try and work through. In practice, New Bamboo don&amp;#8217;t run a separate debug server but just re-provision pre-production very quickly (it&amp;#8217;s there, it&amp;#8217;s got the production code already, it&amp;#8217;s almost ready to go).&lt;/p&gt;

&lt;h2&gt;Martin Kleppmann - Sales &amp;amp; payments in your app&lt;/h2&gt;

&lt;p&gt;The Ruby Invoicing Framework tries to give a solid foundation where you can build a solid web application. It offers a few classes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Invoice - you owe us money&lt;/li&gt;
&lt;li&gt;Credit note - oops you billed to much&lt;/li&gt;
&lt;li&gt;Payment -thanks for the cash&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They all inherit from LedgerItem which throws is a naming throw back to accounting terms, but it essentially offers you access to discrete billing line items and two companies, one on each side of the transaction. What it gives you for free is an automatically rendered invoice (an actual invoice you can post/email to the client) with all the legally required fields included.&lt;/p&gt;

&lt;p&gt;Accountant need to know the exact dates of transactions, that bank statements reconciling, and VAT/sales or other applicable taxes.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;insert various slides about accounting practices and how ledger items are calculated. I&amp;#8217;d expect anybody who runs their own business already knows all this stuff&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I&amp;#8217;ve had to roll most of this stuff myself over the past 12-18 months, and for the most part our schemas and approach is almost identical. Just to save myself the maintenance headache, I&amp;#8217;ll probably look at porting over to this invoicing gem. I also need to look at the existing open standards (UBL, XBRL-GL, OAccounts, and OASIS) and see how they fit in.&lt;/p&gt;

&lt;h2&gt;George Palmer - CouchDB and Ruby&lt;/h2&gt;

&lt;p&gt;I&amp;#8217;ve covered the background on what CouchDB is in previous posts, or it&amp;#8217;s otherwise readily available with a quick search so I&amp;#8217;ll leave you to find the really high level stuff out yourself. Documents are stored as JSON, you get subsets of documents via views. Done.&lt;/p&gt;

&lt;p&gt;Unlike relational databases which you pay a performance hit on indexes when you insert data, CouchDB makes you pay the penalty on the first read of a view. That means if you path insert a bunch of data, the next request to see that information is going to be slow. In a typical read heavy web application, this isn&amp;#8217;t that noticeable. If you&amp;#8217;re inserting data often, then there are ways to mitigate the problem.&lt;/p&gt;

&lt;p&gt;So when should you use CouchDB? If you want a schema-less database. FriendFeed was used as an example, given the amount of data they store adding a new column in MySQL could take several hours. Often it also relates more directly to your real-world models. George went through an example from constructing his &amp;#8220;5ft Shelf&amp;#8221; site and the complexities associated with books (numerous ISBNs, different titles in different countries, difference retail prices in different locales, different editions, etc.) and how difficult it is to map all these permutations in a relational system. The final scenario is when you know you&amp;#8217;re going to need replication or sharding, either for offline capability or scaling.&lt;/p&gt;

&lt;p&gt;You shouldn&amp;#8217;t use it is when your problem domain is very fixed. Finance is a good example, real estate transactions are another.&lt;/p&gt;

&lt;p&gt;Enter George&amp;#8217;s couch_foo plugin which is a way of interfacing with CouchDB in an ActiveRecord fashion. Because everything is ultimately stored as JSON, there&amp;#8217;s no real concept of datatypes. So long as the attributes can be translated to JSON and reconstructed from JSON you&amp;#8217;re good to use any datatype/object you type. But the plugin does natively support validations, associations, callbacks, and pretty much everything you&amp;#8217;d expect from AR. One gotcha is that if you try and order by an attribute that isn&amp;#8217;t exposed by the key, it will return &lt;em&gt;all&lt;/em&gt; the results and then order them in ruby. It&amp;#8217;s a double sting if you then try and limit, as you&amp;#8217;ve pulled back a heap of records you never needed.&lt;/p&gt;

&lt;p&gt;Performance wise, George said there&amp;#8217;s a whole heap of naive benchmarks claiming CouchDB is faster than this or that. It&amp;#8217;s a different approach, some things are going to be quicker, some are going to be slower. The latest CouchDB release (0.9) does offer some speed improvements over previous versions though.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/rubypond/~4/e7IY0KTF4ls" height="1" width="1"/&gt;</description>
          <pubDate>Fri, 24 Jul 2009 08:54:50 GMT</pubDate>
          <guid isPermaLink="false">http://rubypond.com/articles/2009/07/24/rails-underground---day-1/</guid>
          <link>http://feedproxy.google.com/~r/rubypond/~3/e7IY0KTF4ls/</link>
        <feedburner:origLink>http://rubypond.com/articles/2009/07/24/rails-underground---day-1/</feedburner:origLink></item>
    
        <item>
          <title>Scotland on Rails 09 - Videos are up</title>
          <description>&lt;p&gt;Not much more to say really, the title sums it up. For those that weren&amp;#8217;t there or those that were that just want a recap go and check out the &lt;a href="http://www.engineyard.com/blog/community/scotland-on-rails/"&gt;Scotland on rails videos&lt;/a&gt; now.&lt;/p&gt;

&lt;p&gt;And I &lt;em&gt;will&lt;/em&gt; eventually get around to doing that round-up of the post/out-of-conference activities to do should you be looking to attend the conference yourself next year.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/rubypond/~4/uvunOx_HEPs" height="1" width="1"/&gt;</description>
          <pubDate>Thu, 21 May 2009 10:41:37 GMT</pubDate>
          <guid isPermaLink="false">http://rubypond.com/articles/2009/05/21/scotland-on-rails-09---videos-are-up/</guid>
          <link>http://feedproxy.google.com/~r/rubypond/~3/uvunOx_HEPs/</link>
        <feedburner:origLink>http://rubypond.com/articles/2009/05/21/scotland-on-rails-09---videos-are-up/</feedburner:origLink></item>
    
        <item>
          <title>Belated Scotland on Rails round up</title>
          <description>&lt;p&gt;Okay, so I&amp;#8217;m almost a month and a half late in finishing this. And the content might already seem a little dated given &lt;a href="http://www.railsconf.com/"&gt;RailsConf&lt;/a&gt; is currently on in Vegas and &lt;a href="http://euruko2009.org/"&gt;EuRuKo&lt;/a&gt; kicks off this weekend in Barcelona. But better late than never.&lt;/p&gt;

&lt;h2&gt;The Summary&lt;/h2&gt;

&lt;p&gt;So in summary, how was it? Pretty good and great value. It wasn&amp;#8217;t blow my mind amazing, but there was enough good stuff going on to justify the trip. Plus Scotland is a beautiful country, and Edinburgh in-particular a great city, so any means of justifying a business expense to get&amp;nbsp;there&amp;nbsp;is&amp;nbsp;worthwhile&amp;nbsp;;)&lt;/p&gt;

&lt;p&gt;So onto the sessions I saw or chatted in depth to people about.&lt;/p&gt;

&lt;h2&gt;Sessions&lt;/h2&gt;

&lt;h3&gt;Keynote - Marcel Molina Jr&lt;/h3&gt;

&lt;p&gt;I&amp;#8217;d heard of Marcel before, I&amp;#8217;d even seen his name grace the cover of some books I own, but I didn&amp;#8217;t know much about him beyond that. For those like me, he was one of the original core team at 37signals that worked on Rails. And his keynote was quite a personal look at the beginnings of the framework. Digging up old usenet posts from DHH showing how he&amp;#8217;s trying to learn ruby, to slowly building the hype around rails, to a public release. For a presentation that was essentially reading old emails and newsgroup postings I found it thoroughly enjoyable. It was particularly interesting to see just how quickly DHH went from new to ruby to releasing probably one of the most widely used tools written in the language.&lt;/p&gt;

&lt;h3&gt;Implementing Geo-awareness - Jim Remsik&lt;/h3&gt;

&lt;p&gt;I&amp;#8217;d had big hopes for this presentation. I&amp;#8217;ve dabbled with geo-awareness stuff before, heck I&amp;#8217;ve even written my own rails mapping plugin because I thought YM4R and the alternatives weren&amp;#8217;t very ruby (btw. it&amp;#8217;s in use for the &lt;a href="http://map.ly/"&gt;collaborative mapping/direction&lt;/a&gt; site I quickly rustled up for my girlfriend &lt;a href="http://map.ly/"&gt;Map.ly&lt;/a&gt;). I was really looking at some idea re how to make an existing site more &amp;#8220;geo aware&amp;#8221;, and this presentation was little more than &amp;#8220;how to put a google map on your site 101&amp;#8221;. For all my complaints about the existing plugins and their approach, they&amp;#8217;re still quite simple to use. With any level of ruby/rails knowledge, this talk could have probably been reduced to 5-10mins.&lt;/p&gt;

&lt;h3&gt;Getting Git: Getting effective project management - Scott Chacon&lt;/h3&gt;

&lt;p&gt;Scott is part of the team at Logical Awesome working on Github. He&amp;#8217;s also done the peepcode on git, and maintains the community book. He really knows his git, and by all accounts I&amp;#8217;m disappointed I missed this one for the geo-awareness session. Thankfully the guys I pair with most days, &lt;a href="http://www.effectif.com"&gt;Graham Ashton&lt;/a&gt; and Jon Ramsey, went and filled me in somewhat on it. The first half was a bit of an introduction on what it is, why it&amp;#8217;s different, how to use it, etc. that could mostly be ignored if you&amp;#8217;ve used it at any length. Apparently the second half of the talk (which unfortunately he tried to cram into 10mins) was amazing. Showing how to use bisect to identify which commit introduced a bug as one of the highlights. The slides are available at &lt;a href="http://github.com/schacon/git-presentations/tree/master"&gt;github&lt;/a&gt; and hopefully the videos will be up soon.&lt;/p&gt;

&lt;h3&gt;Ruby on the couch eating potatoes - Alexander Lang&lt;/h3&gt;

&lt;p&gt;A well times presentation for my perspective. I&amp;#8217;ve been looking at a number of relational database alternatives of late for one of my side projects, and &lt;a href="http://couchdb.apache.org/"&gt;CouchDB&lt;/a&gt; had come up as one of the most likely contenders. So I&amp;#8217;d done a fair degree of research on CouchDB itself, but not much on the most suitable adapter for using it with rails.&lt;/p&gt;

&lt;p&gt;Alex did a good job going through all the various know adapters for talking to CouchDB (including his own, CouchPotato) and pointed out the pros and cons of each. Amazingly for a presentation like this he admitted that you should avoid his. For those interested, the winner was Paul Carey&amp;#8217;s &lt;a href="http://github.com/paulcarey/relaxdb/tree/master"&gt;RelaxDB&lt;/a&gt;. A point of contention which spilled onto the twittersphere was Alex&amp;#8217;s recommendation to avoid George Palmer&amp;#8217;s &lt;a href="http://github.com/georgepalmer/couch_foo/tree/master"&gt;CouchFoo&lt;/a&gt; (George was in the audience). The rationale being that CouchFoo, much like CouchPotato, is too &amp;#8220;ActiveRecord&amp;#8221; and that document-based databases are different to relational databases so this means the approach must be inherently wrong.&lt;/p&gt;

&lt;p&gt;Personally I think no, the rationale is wrong. Sure, you don&amp;#8217;t want to treat CouchDB like a relational database because you&amp;#8217;re missing the benefits gained in the document approach and kind of missing the whole point. But that doesn&amp;#8217;t mean ActiveRecord for CouchDB is wrong. ActiveRecord does not &lt;em&gt;have to&lt;/em&gt; mean Object Relational Mapper, it&amp;#8217;s just up until this point it&amp;#8217;s all people have used it for. In fact, there is is nothing in the name ActiveRecord that inherently infers it is only for relational schemas. I think if people had grown up being accustomed to AR being an Object Document Mapper instead of an ORM then this argument would be moot. In rails you interface with an object, the adapter takes care of how to store it be-it in a document store or a relational store. We&amp;#8217;ve got a pretty well documented API for AR, with lots of validation rules and other plugins that are useful. If you then decide to go build a relational schema in your document database that isn&amp;#8217;t the fault of CouchFoo, just like AR can&amp;#8217;t be blamed if you decide to try and keep everything in a single big flat table in MySql. &lt;/p&gt;

&lt;p&gt;My own conclusion after some research was the two best adapters out there were RelaxDB and CouchFoo. RelaxDB seems the best (I&amp;#8217;ll go into details in a future post) but CouchFoo is ideal if you want to transition quickly or need access to some of the AR helpers/mixins. So all in all, a good presentation and made me go back and re-assess my own research on the topic and we ultimately shared the same conclusion albeit with different reasoning.&lt;/p&gt;

&lt;h3&gt;Merb and Rails 3.0 - Yehuda Katz&lt;/h3&gt;

&lt;p&gt;Probably not a lot to go through here which wont be out of date due to RailsConf. An interesting and high-paced presentation from Yehuda on what&amp;#8217;s coming in rails 3.0, some of the design decisions, etc. Probably not a huge amount of new information for anybody that had been using merb or following the public updates.&lt;/p&gt;

&lt;h3&gt;In Praise of non-fixtured data - Kevin Barnes&lt;/h3&gt;

&lt;p&gt;I often wonder how I&amp;#8217;d go on stage in front of a bunch of peers, but I&amp;#8217;m thankful that many of the presenters at these conferences looks as nervous up there as I&amp;#8217;d be. Kevin made me rethink if I should ever bother. A really excellent and clear presenter that was a joy to listen to.&lt;/p&gt;

&lt;p&gt;That being said, I didn&amp;#8217;t take from this as much as I&amp;#8217;d hoped. It&amp;#8217;s my own fault as the talk was exactly what was presented on the label, mostly about why you shouldn&amp;#8217;t use fixtured data and then a look at what the alternatives are. I was looking for a bit more of a detailed comparison of the pros/cons of each of the object factory approaches (much like Alex had done in the CouchDB talk) but it was really just a cursory mention of them all and then a look at how to use the one Kevin had developed, ObjectDaddy.&lt;/p&gt;

&lt;h3&gt;Confessions of a PackRat - Scott Raymond&lt;/h3&gt;

&lt;p&gt;I only caught the tail-end of this talk, but from those I spoke to it was quite insightful. Scott went into detail about how the developed a Facebook app and then needing to scale quickly to deal with the load. Everything from scaling into EC2, switching between technologies, and having to handle people trying to game the system. The 10 mins I saw were thoroughly enjoyable.&lt;/p&gt;

&lt;h3&gt;Advanced Deployment - Johnathon Weiss&lt;/h3&gt;

&lt;p&gt;This is one that I felt wasn&amp;#8217;t at all what was described on the label. I&amp;#8217;d normally consider deployment something more akin to &amp;#8220;getting your stuff onto servers&amp;#8221;. Instead this covered lots of high-level architectural options you have available to you, the alternatives for each, but without any real explanation on why you should choose one over another. An interesting topic, but pitched to the wrong crowd with the wrong name I feel. Much better for manager types with little-to-no hands on rails experience. I think anybody who&amp;#8217;s actually deployed an application would have known the answers to all the theoretical questions raise.&lt;/p&gt;

&lt;h3&gt;The Ruby Object Model - Dave Thomas&lt;/h3&gt;

&lt;p&gt;The guy that brought is the Pragmatic Programmers and a number of ruby books and videos from that stable gave a great presentation on the object model with ruby. The shoeless maestro strutted around the stage and made what could be a very dull and overly technical topic seem approachable and simplistic. When you call a method where does it look to first to run it? Where does it bubble up to? In what order? And how do you access an anonymous method? All this arnd more was answered! Not sure if I picked up anything new, but it was a great cementing of my understanding in a highly graphical fashion. 6 weeks on when I&amp;#8217;m debugging the internals of something I&amp;#8217;m still visualising Dave&amp;#8217;s charts in my head to help me. Much of the content you can go through at leisure and in more detail in the $5 videos from &lt;a href="http://www.pragprog.com/screencasts/v-dtrubyom/the-ruby-object-model-and-metaprogramming"&gt;pragprog&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;R-house - Fernand Galiana&lt;/h3&gt;

&lt;p&gt;Fernand took the noble risk of trying to do a live demonstration of the possibilities of using home automation with ruby. Thankfully for the most part it worked, and when it didn&amp;#8217;t he was entertaining enough to cover it and continue. And I have to appreciate the level of work that has gone into R-house and the libraries it depends on to make this happen, with such a range of different home automation equipment out there and each with there own slight nuances on how to interface it can&amp;#8217;t have been a simple job. A great presentation, not sure how soon I&amp;#8217;ll be able to use any of it though.&lt;/p&gt;

&lt;h3&gt;How to makes a successful Rails app - Steven A Bristol&lt;/h3&gt;

&lt;p&gt;A presentation with no slides?! The horror!! Steven was another excellent presenter, and with a great presentation to boot. He went into what it takes to actually make a successful Rails app (hint: actually releasing it is a big part), and the advice was sound. The other big one which I think many developers ignore, I know I&amp;#8217;ve been guilty of it, is trying to do the design yourself. Pay for a few days of their time and make it look professional. He was awfully opinionated on a number of topics without any real basis. That was fine when I agreed with him, I thought he was full of shit when he didn&amp;#8217;t justify it ;) One point that niggled at me throughout most of the presentation was that the reason I know of Steven and his company was through the the open-source &lt;a href="http://lovdbyless.com/"&gt;Lovd by Less&lt;/a&gt; social networking framework they&amp;#8217;d built in rails. I&amp;#8217;d needed something like it once upon a time and looked at Lovd, and the code was horrible. So here I was being lectured by someone on how to make a Rails app who&amp;#8217;d produced some of the ugliest code I&amp;#8217;d tried to use. &lt;/p&gt;

&lt;p&gt;Then the bombshell hit (paraphrasing), &amp;#8220;I don&amp;#8217;t care what the code looks like if it works, just get it out the door and see if people like it. Anybody who&amp;#8217;s seen some of our code will know it&amp;#8217;s horrible&amp;#8221;. And he was right, on all counts. But&amp;#8230; I still had hesitations reservations. This was an open source framework, so the consumers in this instance are developers. So for the same reasons that you should get a designer to make your site look good for regular punters, I think you should take the time to make your code elegant and usable for developers. He&amp;#8217;s got a great looking product which does what it needs to, it&amp;#8217;s a shame that it&amp;#8217;s such a pain to use and made me question the validity of what he was saying. To that end, and rather than continuing to pay other developers to extend my own social networking engine I&amp;#8217;ve taken it upon myself to fix everything I think is broken with Lovd. There&amp;#8217;s still a long way to go and I&amp;#8217;ll make a more formal announcement once it&amp;#8217;s nearer completion but it&amp;#8217;s already come a long way IMHO. Get it from &lt;a href="http://github.com/rubypond/lovd-by-less/tree"&gt;github&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;Who needs Photoshop? Image manipulation in Ruby - Cory Forsyth&lt;/h3&gt;

&lt;p&gt;Reading through this initially I thought it was going was a bit of a sensational headline for what was going to be an intermediate level look at rmagick. And given my own experience with rmagick, I couldn&amp;#8217;t possibly stomach the thought of it ever being considered a viable photoshop replacement. I was wrong.&lt;/p&gt;

&lt;p&gt;This was another thoroughly entertaining and informative presentation. Cory went through in detail the logic behind the one feature that has been making me want to upgrade to the latest version of photoshop, &lt;a href="http://www.youtube.com/watch?v=019mu8FTy6M"&gt;content aware scaling&lt;/a&gt;, complete with code samples. Next up was facial recognition which could be applied to suggest smart cropping, or apparently put party hats on cats. It&amp;#8217;s most likely made my CS4 upgrade redundant, which paid for the trip numerous times over.&lt;/p&gt;

&lt;h3&gt;Edgecase dialog: Ruby code review - Joe O&amp;#8217;Brian and Jim Weirich&lt;/h3&gt;

&lt;p&gt;It sounded like a boring topic, it should have been a boring topic, and a very odd note on which to end a conference. It turns out Joe and Jim are brilliant entertainers taking the audience through a hypothetical role play of a consultant coming in to refactor an existing rails code base. While it&amp;#8217;s unlikely they&amp;#8217;ll be invited back to do their return for the Edinburgh Fringe Festival in August, they thoroughly deserved their rapturous applause at the end. I&amp;#8217;ve not got any notes unfortunately, I&amp;#8217;m not sure if that&amp;#8217;s because I was laughing too hard or most of it was common sense. In any event, it was a great reminder that you should step back and look at you code to make sure you aren&amp;#8217;t trying to be clever for the sake of it. Simple solutions are often the best.&lt;/p&gt;

&lt;h2&gt;Seeya next year&lt;/h2&gt;

&lt;p&gt;It was well worth the trip. The conference was super cheap so it&amp;#8217;s really not hard to justify the expense and Edinburgh is a great place to visit. I&amp;#8217;ve been there as both a tourist and lived there for 12 months, so I&amp;#8217;ll do a subsequent post for those looking to attend next year with my own off-conference highlights.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/rubypond/~4/B4MxJHbkt1g" height="1" width="1"/&gt;</description>
          <pubDate>Fri, 08 May 2009 13:15:08 GMT</pubDate>
          <guid isPermaLink="false">http://rubypond.com/articles/2009/05/08/belated-scotland-on-rails-round-up/</guid>
          <link>http://feedproxy.google.com/~r/rubypond/~3/B4MxJHbkt1g/</link>
        <feedburner:origLink>http://rubypond.com/articles/2009/05/08/belated-scotland-on-rails-round-up/</feedburner:origLink></item>
    
        <item>
          <title>Capturing a form cancel</title>
          <description>&lt;p&gt;I&amp;#8217;ve been meaning to write this one up for months now, ever since I originally posted the &lt;a href="http://rubypond.com/articles/2008/07/16/sexy-forms-in-rails/"&gt;sexy forms in rails&lt;/a&gt;. The form builder I talk about in that post makes it easy to put a submit and cancel button on a the form, and a few people have asked how to handle the cancel action. Some have suggested using javascript to redirect back using an onclick, ick! What happens if the user doesn&amp;#8217;t have javascript support? Sure you might try and justify it by saying, &amp;#8220;Well, how many users don&amp;#8217;t have javascript these days?&amp;#8221; but there is absolutely no reason why this &lt;em&gt;has&lt;/em&gt; to use javascript, so lets make it work for &lt;em&gt;everybody&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;It&amp;#8217;s actually much easier than you&amp;#8217;d think, within &lt;em&gt;application.rb&lt;/em&gt; I&amp;#8217;ve got the following:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class ApplicationController &amp;lt; ActionController::Base
  before_filter :catch_cancel, :update =&amp;gt; [:create, :update, :destroy]
  after_filter :set_referrer, :only =&amp;gt; [:index, :show]

  private
    def set_referrer
      session[:referrer] = url_for(params)
    end

    def catch_cancel
      redirect_to session[:referrer] if params[:commit] == &amp;quot;Cancel&amp;quot;
    end
end&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The logic being, that if we&amp;#8217;ve got a fully RESTful architecture the only pages we&amp;#8217;ll ever want to go &amp;#8220;back&amp;#8221; will be the index or show actions on each controller. So we cheekily call url&lt;em&gt;for in an after filter with the current params to get the path of the page we&amp;#8217;ve just display the user, and stash it in the session. Theoretically it may be possible to use ENV[&amp;#8220;HTTP&lt;/em&gt;REFERER&amp;#8221;] instead but it&amp;#8217;s not guaranteed, this approach however ensures we keep track of the page to go back to explicitly ourselves.&lt;/p&gt;

&lt;p&gt;Then, to piece it all together there&amp;#8217;s a before filter checking if the user has clicked the submit button labeled with &amp;#8220;Cancel&amp;#8221;. You&amp;#8217;ll need to change that equality test dependent on what text you put on your button. You may also need to move the before filter into the controllers that require them, depending on the order it needs to run with other filters you have.&lt;/p&gt;

&lt;p&gt;Hope that helps.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/rubypond/~4/3Nu3RZdKoFA" height="1" width="1"/&gt;</description>
          <pubDate>Fri, 06 Feb 2009 22:40:02 GMT</pubDate>
          <guid isPermaLink="false">http://rubypond.com/articles/2009/02/06/capturing-a-form-cancel/</guid>
          <link>http://feedproxy.google.com/~r/rubypond/~3/3Nu3RZdKoFA/</link>
        <feedburner:origLink>http://rubypond.com/articles/2009/02/06/capturing-a-form-cancel/</feedburner:origLink></item>
    
        <item>
          <title>Ruby Manor Videos Available</title>
          <description>&lt;p&gt;For anybody that was unable to attend (like me), or for those that just want a recap then a selection of the &lt;a href="http://rubymanor.org/videos/"&gt;Ruby Manor videos&lt;/a&gt; are now available for download. Hopefully some more will be posted soon.&lt;/p&gt;

&lt;p&gt;For any of the bits that are missing you can read through Graham&amp;#8217;s review of the various sessions. &lt;a href="http://effectif.com/2008/11/22/notes-from-the-ruby-manor"&gt;Ruby Manor part 1&lt;/a&gt; and &lt;a href="http://effectif.com/2008/11/23/notes-from-the-ruby-manor-part-2"&gt;Ruby Manor part 2&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/rubypond/~4/BpYYoZmBDBg" height="1" width="1"/&gt;</description>
          <pubDate>Fri, 16 Jan 2009 14:38:44 GMT</pubDate>
          <guid isPermaLink="false">http://rubypond.com/articles/2009/01/16/ruby-manor-videos-available/</guid>
          <link>http://feedproxy.google.com/~r/rubypond/~3/BpYYoZmBDBg/</link>
        <feedburner:origLink>http://rubypond.com/articles/2009/01/16/ruby-manor-videos-available/</feedburner:origLink></item>
    
        <item>
          <title>scRUBYt! Gets Plugins!</title>
          <description>&lt;p&gt;Yes! You heard right! As you may have gathered, it&amp;#8217;s been a rather frantic month of development for scRUBYt! and currently this is the addition I&amp;#8217;m most proud of. One of the most common requests used to be along the lines of &amp;#8220;when do you plan to support xxx format output?&amp;#8221;. Now, scRUBYt! is oblivious to output formats. That&amp;#8217;s right, it natively supports nothing, nada, zilch. But to make it useful, we&amp;#8217;ve written a Hash output plugin which we&amp;#8217;re shipping it with.&lt;/p&gt;

&lt;h2&gt;How to use a scRUBYt! output plugin&lt;/h2&gt;

&lt;p&gt;Firstly, you need to make sure you have the plugin you require installed. At the time of writing there will only be the two I&amp;#8217;ve written, Hash and XmlFile. Then in your ruby file require the plugin. As the current edge release isn&amp;#8217;t yet packaged as a gem you&amp;#8217;ll need to test this with the github checkout and reference the output plugin explicitly:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;require &amp;quot;plugins/scrubyt_xml_file_output/scrubyt_xml_file_output&amp;quot;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you&amp;#8217;ve been following the tutorials talking about &lt;a href="http://rubypond.com/articles/2009/01/15/web-scraping---the-amazon-example-finale/"&gt;web scraping&lt;/a&gt; with the new version for the past few weeks you&amp;#8217;ll have seen how to direct output to a plugin. To request Hash output it&amp;#8217;s:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@extractor = Scrubyt::Extractor.new :output =&amp;gt; :hash do
    fetch &amp;quot;http://www.google.com/search?&amp;amp;q=ruby&amp;quot;
    result &amp;quot;//html/body/div[5]/div/div/h2/a&amp;quot;
end&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;and for XmlFile it is:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@file = File.open(&amp;quot;results.xml&amp;quot;, &amp;quot;w&amp;quot;)
@extractor = Scrubyt::Extractor.new :output =&amp;gt; :xml_file, :file =&amp;gt; @file do
    fetch &amp;quot;http://www.google.com/search?&amp;amp;q=ruby&amp;quot;
    result &amp;quot;//html/body/div[5]/div/div/h2/a&amp;quot;
end&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The XmlFile output takes an additional parameter which is the file to stream the results out to.&lt;/p&gt;

&lt;h2&gt;Creating your own plugin&lt;/h2&gt;

&lt;p&gt;That&amp;#8217;s great for those of you that are happy with XML or Hash output, but what about if you want some other custom format? Well it&amp;#8217;s time to create your own. I&amp;#8217;ll show you the actual code that implements the XmlFile output to show you how simple it is:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;require 'rexml/document'
require &amp;quot;#{File.dirname(__FILE__)}/inflector&amp;quot;
require &amp;quot;#{File.dirname(__FILE__)}/inflections&amp;quot;

class Scrubyt::Output::XmlFile &amp;lt; Scrubyt::Output::Plugin  
  @subscribers = {}
  on_initialize :setup_file
  before_extractor :open_root_node
  after_extractor :close_root_node
  on_save_result :save_xml


  def setup_file(args = {})
    @file = args[:file]
  end

  def open_root_node(*args)
    @file.write(&amp;quot;&amp;lt;root&amp;gt;&amp;quot;)
  end

  def save_xml(name, results)
    if results.is_a?(::Hash)
      @file.write results.to_xml
    else
      results.each do |result|
        @file.write result.to_xml(name)
      end
    end
  end

  def close_root_node(*args)
    @file.write(&amp;quot;&amp;lt;/root&amp;gt;&amp;quot;)
  end
end&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The require lines at the top are only needed for this output format. REXML to construct the XML tags for me, and some inflections I&amp;#8217;ve put together to turn the Hash and Array objects into XML. Now into analysing the class proper.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class Scrubyt::Output::XmlFile &amp;lt; Scrubyt::Output::Plugin  
  @subscribers = {}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;At the moment, you&amp;#8217;ll need to inintialize this instance variable to be an empty Hash for the events to get attached correctly. I&amp;#8217;m looking for a way to remove it, stay tuned. But for now you&amp;#8217;ll need to put it in.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;on_initialize :setup_file
before_extractor :open_root_node
after_extractor :close_root_node
on_save_result :save_xml&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here we&amp;#8217;ve got four events to listen for, the concept should be familiar if you&amp;#8217;re coming from Rails. Essentially all we are doing is saying &amp;#8220;When we initialize run the setup&lt;em&gt;file method. Before the extractor actually starts, run the method called open&lt;/em&gt;root&lt;em&gt;node. Whenever we get a result to save, call save&lt;/em&gt;xml. And finally, after the extractor run the method called close&lt;em&gt;root&lt;/em&gt;node.&amp;#8221;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def setup_file(args = {})
  @file = args[:file]
end&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is fairly straightforward. If you&amp;#8217;ve got any custom logic that needs to happen when the output plugin is initialized you can place it in here. Any parameter that is passed in to Extractor.new() is passed through for you to access here.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def open_root_node(*args)
  @file.write(&amp;quot;&amp;lt;root&amp;gt;&amp;quot;)
end&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now just to open the XML file, and keep it somewhat consistent with the old scRUBYt! XML output we open a &lt;root&gt; node within the file.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def save_xml(name, results)
  if results.is_a?(::Hash)
    @file.write results.to_xml
  else
    results.each do |result|
      @file.write result.to_xml(name)
    end
  end
end&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here is where the majority of the magic happens. The save_xml method will be passed the desired name for the result, and a hash of the results. This is essentially the same format you&amp;#8217;d get if you used the Hash output format, except for each individual detail block rather than then entire extractor.&lt;/p&gt;

&lt;p&gt;The reason for the if/else scenario is for when results are not part of a detail block. If you&amp;#8217;re just returning results straight (like the Google example at the top of this post) then &amp;#8220;results&amp;#8221; in this context will be a list/Array of all the matching results rather than a Hash.&lt;/p&gt;

&lt;h2&gt;Passing results back to the extractor&lt;/h2&gt;

&lt;p&gt;Not everyone is going to want to stream results out to a file though, so to deal with this you can make a results method available on the instance of your plugin. As I said earlier, even Hash operates as a plugin now so we can see an example of how this work in the Hash output plugin:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class Scrubyt::Output::Hash &amp;lt; Scrubyt::Output::Plugin
  @subscribers = {}
  on_initialize :setup_results
  on_save_result :store_hash

  def setup_results(args = {})
    @results = []
  end

  def results
    @results
  end

  def store_hash(name, passed_results)
    @results &amp;lt;&amp;lt; passed_results
  end
end&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here we setup a @results instance on&lt;em&gt;initialize, and then on&lt;/em&gt;save&lt;em&gt;results simply pushes the passed&lt;/em&gt;results into @results. Confused yet? Hopefully the code is clear enough to make sense.&lt;/p&gt;

&lt;p&gt;All that happens then is that back in your extractor definition the call to @extractor.results is passed through to the first output plugin it can find.&lt;/p&gt;

&lt;h2&gt;Naming Conventions and Namespacing&lt;/h2&gt;

&lt;p&gt;The only additional requirement for a plugin to work in scRUBYt! is that it is correctly named and namespaced. As you may have noticed the ones I&amp;#8217;ve provided are called Scrubyt::Output::Hash and Scrubyt::Output::XmlFile, that means they can be targeted using :output =&gt; :hash and  :output =&gt; :xml&lt;em&gt;file respectively. If you wanted to call your output GlennsBadExample it would be namespaced as Scrubyt::Output::GlennsBadExample and you&amp;#8217;d then just need to require the appropriate file and use :output =&gt; :glenns&lt;/em&gt;bad_example&lt;/p&gt;

&lt;h2&gt;Oh the possibilities! So what&amp;#8217;s next?&lt;/h2&gt;

&lt;p&gt;We&amp;#8217;re only just starting to see the possibilities that this will offer our extractors. It opens up the possibility of pushing results not only to a different format, but possibly a completely different service. It&amp;#8217;s now trivial to create an output format that streams results directly into backgroundRB, a nanite worker, or a web service for further processing and data warehousing. By the time you read this, you&amp;#8217;ll also be able to pass in an array of outputs like :output =&gt; [:hash, :xml_file] and have both plugins generate the appropriate format(s). For the scraper I&amp;#8217;m currently working on where I have two different companies wanting the same data, this could be just the ticket for interfacing directly to their API as I scrape.&lt;/p&gt;

&lt;p&gt;I&amp;#8217;d love to hear what ideas people might have for this, or how you think it could be improved. We&amp;#8217;re really hopeful that this is the kind of thing that makes developing and extending scRUBYt! really easy for those with more complicated needs.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/rubypond/~4/gMWBj9onwkg" height="1" width="1"/&gt;</description>
          <pubDate>Fri, 16 Jan 2009 01:18:45 GMT</pubDate>
          <guid isPermaLink="false">http://rubypond.com/articles/2009/01/16/scrubyt-gets-plugins/</guid>
          <link>http://feedproxy.google.com/~r/rubypond/~3/gMWBj9onwkg/</link>
        <feedburner:origLink>http://rubypond.com/articles/2009/01/16/scrubyt-gets-plugins/</feedburner:origLink></item>
    
        <item>
          <title>Web Scraping - The Amazon Example Finale</title>
          <description>&lt;p&gt;So yesterday I showed you a fairly contrived example of &lt;a href="http://rubypond.com/articles/2009/01/13/more-web-scrapers-with-the-upcoming-scrubyt/"&gt;how to build a web scraper&lt;/a&gt; using scRUBYt! to get data from Amazon (they&amp;#8217;ve got an API that would be much easier and more robust if you need access to this info, but that&amp;#8217;s not the point at the moment). But if you look at the results, they&amp;#8217;re not the greatest. There is too much noise in some fields, and we probably want to share there data with another system so a ruby based Hash object isn&amp;#8217;t going to work.&lt;/p&gt;

&lt;h2&gt;Removing empty results with scRUBYt!&lt;/h2&gt;

&lt;p&gt;For various reasons, sometimes you may not get all the data you want back for every record. It&amp;#8217;s usually related to your result definition being too restrictive, or a change in format on a specific page. Maybe the price information is in a different DIV if it is on sale. In any event, you need to make a decision on what to do. In the new release of scRUBYt! there are three immediate options that come to mind, but we may well build more in if required. First, and what we did yesterday in the example, is to do nothing. You&amp;#8217;ll get the nil/empty result returned back to you to handle as you see fit. Second, you can simply drop any fields that are nil (I&amp;#8217;ve just displayed the first few results):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@extractor = Scrubyt::Extractor.new do
  fetch &amp;quot;http://www.amazon.com/&amp;quot;
  select_option &amp;quot;url&amp;quot;, &amp;quot;Books&amp;quot;
  fill_textfield &amp;quot;field-keywords&amp;quot;, &amp;quot;ruby&amp;quot;
  submit
  book_detail &amp;quot;//td[@class='dataColumn']/table/tr/td/a&amp;quot; do
    title &amp;quot;//h1[@class='parseasinTitle']&amp;quot;
    price &amp;quot;//b[@class='priceLarge']&amp;quot;, :remove_blank =&amp;gt; true
    saving &amp;quot;//td[@class='price']&amp;quot;
    isbn &amp;quot;//li[text()*='ISBN-10:']&amp;quot;
  end
end
puts @extractor.results.inspect
=&amp;gt; [{:book=&amp;gt;[{:title=&amp;gt;&amp;quot;The Ruby Programming Language [ILLUSTRATED]  (Paperback)&amp;quot;}, 
             {:price=&amp;gt;&amp;quot;$26.39&amp;quot;}, 
             {:saving=&amp;gt;&amp;quot;$13.60\n      (34%)\n    &amp;quot;}, 
             {:isbn=&amp;gt;&amp;quot;ISBN-10: 0596516177&amp;quot;}]}
    {:book=&amp;gt;[{:title=&amp;gt;&amp;quot;The Ruby Programming Language (Paperback)&amp;quot;}, 
             {:saving=&amp;gt;nil}, 
             {:isbn=&amp;gt;&amp;quot;ISBN-10: 020171096X&amp;quot;}]}, 
    {:book=&amp;gt;[{:title=&amp;gt;&amp;quot;Beginning Ruby: From Novice to Professional (Beginning from Novice to Professional) (Paperback)&amp;quot;}, 
             {:price=&amp;gt;&amp;quot;$26.39&amp;quot;}, 
             {:saving=&amp;gt;&amp;quot;$13.60\n      (34%)\n    &amp;quot;}, 
             {:isbn=&amp;gt;&amp;quot;ISBN-10: 1590597664&amp;quot;}]}
    ...]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We&amp;#8217;ve set :remove_blank to true on the price field, and as a result you&amp;#8217;ll see that the 2nd result contains no price element. Alternatively, you could drop any detail block (in this example, any single book) which is missing the field:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@extractor = Scrubyt::Extractor.new do
  fetch &amp;quot;http://www.amazon.com/&amp;quot;
  select_option &amp;quot;url&amp;quot;, &amp;quot;Books&amp;quot;
  fill_textfield &amp;quot;field-keywords&amp;quot;, &amp;quot;ruby&amp;quot;
  submit
  book_detail &amp;quot;//td[@class='dataColumn']/table/tr/td/a&amp;quot; do
    title &amp;quot;//h1[@class='parseasinTitle']&amp;quot;, :required =&amp;gt; true
    price &amp;quot;//b[@class='priceLarge']&amp;quot;, :required =&amp;gt; true
    saving &amp;quot;//td[@class='price']&amp;quot;
    isbn &amp;quot;//li[text()*='ISBN-10:']&amp;quot;
  end
end
puts @extractor.results.inspect
=&amp;gt; [{:book=&amp;gt;[{:title=&amp;gt;&amp;quot;The Ruby Programming Language [ILLUSTRATED]  (Paperback)&amp;quot;},
             {:price=&amp;gt;&amp;quot;$26.39&amp;quot;},
             {:saving=&amp;gt;&amp;quot;$13.60\n      (34%)\n    &amp;quot;},
             {:isbn=&amp;gt;&amp;quot;ISBN-10: 0596516177&amp;quot;}]},
    {:book=&amp;gt;[{:title=&amp;gt;&amp;quot;Beginning Ruby: From Novice to Professional (Beginning from Novice to Professional) (Paperback)&amp;quot;},
             {:price=&amp;gt;&amp;quot;$26.39&amp;quot;},
             {:saving=&amp;gt;&amp;quot;$13.60\n      (34%)\n    &amp;quot;},
             {:isbn=&amp;gt;&amp;quot;ISBN-10: 1590597664&amp;quot;}]},
    {:book=&amp;gt;[{:title=&amp;gt;&amp;quot;Programming Ruby: The Pragmatic Programmers' Guide, Second Edition [ILLUSTRATED]  (Paperback)&amp;quot;},
             {:price=&amp;gt;&amp;quot;$29.67&amp;quot;},
             {:saving=&amp;gt;&amp;quot;$15.28\n      (34%)\n    &amp;quot;},
             {:isbn=&amp;gt;&amp;quot;ISBN-10: 0974514055&amp;quot;}]},
    ...]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This time the &amp;#8220;The Ruby Programming Language (Paperback)&amp;#8221; book isn&amp;#8217;t included in the results at all. For our purposes though, I&amp;#8217;ve decided that I only want to know about books that I have &lt;em&gt;all&lt;/em&gt; the details for. Instead of setting :required on every result, I can specify it on the book_detail definition which will give the same output:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@extractor = Scrubyt::Extractor.new do
  fetch &amp;quot;http://www.amazon.com/&amp;quot;
  select_option &amp;quot;url&amp;quot;, &amp;quot;Books&amp;quot;
  fill_textfield &amp;quot;field-keywords&amp;quot;, &amp;quot;ruby&amp;quot;
  submit
  book_detail &amp;quot;//td[@class='dataColumn']/table/tr/td/a&amp;quot;, :required =&amp;gt; :all do
    title &amp;quot;//h1[@class='parseasinTitle']&amp;quot;
    price &amp;quot;//b[@class='priceLarge']&amp;quot;
    saving &amp;quot;//td[@class='price']&amp;quot;
    isbn &amp;quot;//li[text()*='ISBN-10:']&amp;quot;
  end
end&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Sanitizing scRUBYt! output&lt;/h2&gt;

&lt;p&gt;The output still isn&amp;#8217;t quite what we want. We&amp;#8217;ve dumped the empty results, but we&amp;#8217;ve still got that ugly &amp;#8220;ISBN-10: &amp;#8221; in front of the ISBN. We could clean it up later, but it&amp;#8217;s creating additional work for ourselves. And if we want this thing to scale (I&amp;#8217;ve got scrapers which scrape thousands of pages off a single site) trying to keep all that data hanging around in memory isn&amp;#8217;t going to work. So let&amp;#8217;s do as much of possible within the scRUBYt! definition as we&amp;#8217;re collecting the data: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@extractor = Scrubyt::Extractor.new do
  fetch &amp;quot;http://www.amazon.com/&amp;quot;
  select_option &amp;quot;url&amp;quot;, &amp;quot;Books&amp;quot;
  fill_textfield &amp;quot;field-keywords&amp;quot;, &amp;quot;ruby&amp;quot;
  submit
  book_detail &amp;quot;//td[@class='dataColumn']/table/tr/td/a&amp;quot;, :required =&amp;gt; :all do
    title &amp;quot;//h1[@class='parseasinTitle']&amp;quot;
    price &amp;quot;//b[@class='priceLarge']&amp;quot;
    saving &amp;quot;//td[@class='price']&amp;quot;
    isbn &amp;quot;//li[text()*='ISBN-10:']&amp;quot;, :script =&amp;gt; Proc.new{|isbn| isbn.gsub(&amp;quot;ISBN-10: &amp;quot;, &amp;quot;&amp;quot;)}
  end
end
puts @extractor.results.inspect
=&amp;gt; [{:book=&amp;gt;[{:title=&amp;gt;&amp;quot;The Ruby Programming Language [ILLUSTRATED]  (Paperback)&amp;quot;},
             {:price=&amp;gt;&amp;quot;$26.39&amp;quot;},
             {:saving=&amp;gt;&amp;quot;$13.60\n      (34%)\n    &amp;quot;},
             {:isbn=&amp;gt;&amp;quot;0596516177&amp;quot;}]},
    ... ]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And now you&amp;#8217;ll see that we&amp;#8217;re getting a much cleaner ISBN result. Just create a Proc, the result will be passed into in and then do as you see fit. An if statement to check it contains something you expect, a regexp, the possibilities are endless. You can also combine this with the other options like :require and :remove_blank. So let&amp;#8217;s really jazz this thing up. We&amp;#8217;ll clean up the saving, and pull in the description and that&amp;#8217;s all the data we need:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@extractor = Scrubyt::Extractor.new do
  fetch &amp;quot;http://www.amazon.com/&amp;quot;
  select_option &amp;quot;url&amp;quot;, &amp;quot;Books&amp;quot;
  fill_textfield &amp;quot;field-keywords&amp;quot;, &amp;quot;ruby&amp;quot;
  submit
  book_detail &amp;quot;//td[@class='dataColumn']/table/tr/td/a&amp;quot;, :required =&amp;gt; :all  do
    title &amp;quot;//h1[@class='parseasinTitle']&amp;quot;
    price &amp;quot;//b[@class='priceLarge']&amp;quot;
    saving &amp;quot;//td[@class='price']&amp;quot;, :script =&amp;gt; Proc.new{|saving| saving.match(/(\$[\d\.]*)/)[1]}
    isbn &amp;quot;//li[text()*='ISBN-10:']&amp;quot;, :script =&amp;gt; Proc.new{|isbn| isbn.gsub(&amp;quot;ISBN-10: &amp;quot;,&amp;quot;&amp;quot;)}
    description &amp;quot;//div[@id='productDescription']//div[@class='content']&amp;quot;
  end
end
puts @extractor.results.inspect
=&amp;gt; [{:book=&amp;gt;[{:title=&amp;gt;&amp;quot;Beginning Ruby: From Novice to Professional (Beginning from Novice to Professional) (Paperback)&amp;quot;},
             {:price=&amp;gt;&amp;quot;$26.39&amp;quot;},
             {:saving=&amp;gt;&amp;quot;$13.60&amp;quot;},
             {:isbn=&amp;gt;&amp;quot;1590597664&amp;quot;}, 
             {:description=&amp;gt;&amp;quot;Product Description\n  Ruby is perhaps best known as the engine powering the...&amp;quot;}]}, 
    {:book=&amp;gt;[{:title=&amp;gt;&amp;quot;Programming Ruby: The Pragmatic Programmers' Guide, Second Edition [ILLUSTRATED]  (Paperback)&amp;quot;}, 
             {:price=&amp;gt;&amp;quot;$29.67&amp;quot;}, 
             {:saving=&amp;gt;&amp;quot;$15.28&amp;quot;}, 
             {:isbn=&amp;gt;&amp;quot;0974514055&amp;quot;}, 
             {:description=&amp;gt;&amp;quot;Product Description\n  Ruby is an increasingly popular, fully object-oriented dynamic...&amp;quot;}]},
    ...]&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Outputting results to XML&lt;/h2&gt;

&lt;p&gt;Inevitably there comes a time where you want to consume this data in something other than your ruby application. At that point, passing around a Hash is probably not the best idea. Alternatively, you might have a scraper that has to scrape hundreds to thousands of pages. Storing all the results in a Hash as you go will bring your machine to it&amp;#8217;s knees. So here comes one of the largest changes to the way the new release of scRUBYt! works.&lt;/p&gt;

&lt;p&gt;Previously, you always had results returned as a Hash and/or XML depending on your need. Everything was held in memory until you destroyed your extractor. Now, the standard XML option is to stream the results out to a file as they are processed and remove them from memory. There is no way to retrieve the results as XML within your program, they have to be streamed out to a file (and really, why would you want XML within your app when you can have native ruby structures instead?). So to save our scraper above out to an XML file you just pass a new output format and an instance of a File to the extractor:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@file = File.new(&amp;quot;amazon_results.xml&amp;quot;, &amp;quot;w&amp;quot;)
@extractor = Scrubyt::Extractor.new :output =&amp;gt; :xml_file, :file =&amp;gt; @file do
  fetch &amp;quot;http://www.amazon.com/&amp;quot;
  select_option &amp;quot;url&amp;quot;, &amp;quot;Books&amp;quot;
  fill_textfield &amp;quot;field-keywords&amp;quot;, &amp;quot;ruby&amp;quot;
  submit
  book_detail &amp;quot;//td[@class='dataColumn']/table/tr/td/a&amp;quot;, :required =&amp;gt; :all  do
    title &amp;quot;//h1[@class='parseasinTitle']&amp;quot;
    price &amp;quot;//b[@class='priceLarge']&amp;quot;
    saving &amp;quot;//td[@class='price']&amp;quot;, :script =&amp;gt; Proc.new{|saving| saving.match(/(\$[\d\.]*)/)[1]}
    isbn &amp;quot;//li[text()*='ISBN-10:']&amp;quot;, :script =&amp;gt; Proc.new{|isbn| isbn.gsub(&amp;quot;ISBN-10: &amp;quot;,&amp;quot;&amp;quot;)}
    description &amp;quot;//div[@id='productDescription']//div[@class='content']&amp;quot;
  end
end&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now a call to @extractor.results at the end of the scrape will return no results. I hope that wasn&amp;#8217;t too much and it&amp;#8217;s given you a good view into how to create your very own web scraper. If you have any questions, head on over to the &lt;a href="http://www.scrubyt.org/"&gt;scRUBYt! forums&lt;/a&gt; or post them in the comments.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/rubypond/~4/eGbApyAbWo8" height="1" width="1"/&gt;</description>
          <pubDate>Thu, 15 Jan 2009 20:52:12 GMT</pubDate>
          <guid isPermaLink="false">http://rubypond.com/articles/2009/01/15/web-scraping---the-amazon-example-finale/</guid>
          <link>http://feedproxy.google.com/~r/rubypond/~3/eGbApyAbWo8/</link>
        <feedburner:origLink>http://rubypond.com/articles/2009/01/15/web-scraping---the-amazon-example-finale/</feedburner:origLink></item>
    
        <item>
          <title>More web scrapers with the upcoming scRUBYt!</title>
          <description>&lt;p&gt;In the previous articles I gave a brief glimpse at the upcoming scRUBYt! release. We learned how to do some basic &lt;a href="http://rubypond.com/articles/2008/12/09/web-spidering-and-data-extraction-with-scrubyt/"&gt;html scraping&lt;/a&gt;, and then followed it up by &lt;a href="http://rubypond.com/articles/2008/12/17/web-spider-creation-with-scrubyt---part-ii/"&gt;scraping multiple pages&lt;/a&gt;. As promised, in this installment I&amp;#8217;ll go over how to get more detailed logging output to help you debug your scraper while in development and how to submit and navigate forms.&lt;/p&gt;

&lt;h2&gt;Logging Scraper Output&lt;/h2&gt;

&lt;p&gt;There&amp;#8217;s been quite a fundamental change to the way scRUBYt! works internally of late. It doesn&amp;#8217;t manifest itself visually in the way you interface to it, but it does mean logging the output is now much easier and cleaner. Using our most basic example from the first tutorial, you just need to pass the :log_level into the extractor:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@extractor = Scrubyt::Extractor.new(:log_level =&amp;gt; :verbose) do
  fetch &amp;quot;http://www.google.com/ncr&amp;quot;
end&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;By default, the output is directed to stdout so you&amp;#8217;d see the following on your screen:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;start
fetch: http://www.google.com/ncr
end&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you&amp;#8217;re after a more complex example, here is the scraper definition from the second part of the tutorial series and the corresponding log output:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@extractor = Scrubyt::Extractor.new(:log_level =&amp;gt; :verbose) do
  fetch &amp;quot;http://www.google.com/ncr&amp;quot;
  fill_textfield &amp;quot;q&amp;quot;, &amp;quot;ruby&amp;quot;
  submit
  page_detail &amp;quot;//h3[@class='r']/a&amp;quot; do
    title &amp;quot;//title&amp;quot;
    summary &amp;quot;//p&amp;quot;, :script =&amp;gt; Proc.new{|result| result if result.match(%r{(\w+\W+){25}})}
  end
  next_page &amp;quot;//a[text()*='Next']&amp;quot;, :limit =&amp;gt; 2
end&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;start
fetch: http://www.google.com/ncr
textfield: 'q' = 'ruby'
with options ''
submit
next detail: 'page' = 'http://ruby-lang.org/'
with args: ''
next detail: 'page' = 'http://en.wikipedia.org/wiki/Ruby_(programming_language)'
with args: ''

etc...

next page: /search?hl=en&amp;amp;ie=UTF-8&amp;amp;q=ruby&amp;amp;start=10&amp;amp;sa=N
fetch: http://www.google.com/search?hl=en&amp;amp;ie=UTF-8&amp;amp;q=ruby&amp;amp;start=10&amp;amp;sa=N
next detail: 'page' = 'http://www.rubycentral.com/book/'
with args: ''

etc...

end&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;At present the valid :log_level values are :none, :critical, :error, :warn, :info, :debug, and :verbose (in increasing order of noise). If you want to direct the log output to something other than stdout the only way at the moment is to override the Scrubyt::Logger#log method. I&amp;#8217;m looking at ways to make it easier to substitute in a file based or other logging approach.&lt;/p&gt;

&lt;h2&gt;Making Your Scraper Navigate Forms&lt;/h2&gt;

&lt;p&gt;So now that we know how to log the output, let&amp;#8217;s do something more useful with our scraper. We can&amp;#8217;t really take our Google example from previous posts any further given how simple their interface is, so let us move over to Amazon. Say I wanted to grab a list the books on ruby that are for sale. Sure, I could probably get this information via an Amazon API&amp;#8230; but that&amp;#8217;s not really the point now is it ;)&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@extractor = Scrubyt::Extractor.new(:log =&amp;gt; :debug) do
  fetch &amp;quot;http://www.amazon.com/&amp;quot;
  select_option &amp;quot;url&amp;quot;, &amp;quot;Books&amp;quot;
  fill_textfield &amp;quot;field-keywords&amp;quot;, &amp;quot;ruby&amp;quot;
  submit
  book_detail &amp;quot;//td[@class='dataColumn']/table/tr/td/a&amp;quot; do
    title &amp;quot;//h1[@class='parseasinTitle']&amp;quot;
    price &amp;quot;//b[@class='priceLarge']&amp;quot;
    saving &amp;quot;//td[@class='price']&amp;quot;
    isbn &amp;quot;//li[text()*='ISBN-10:']&amp;quot;
  end
end&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So let&amp;#8217;s run through what we&amp;#8217;ve got here, hopefully some of it looks familiar from the previous examples we&amp;#8217;ve gone through. First we fetch the page to start with, then in the select field named &amp;#8220;url&amp;#8221; we choose the options that says &amp;#8220;Books&amp;#8221;, and we then submit the form. scRUBYt! will keep track of the last form you input any data to, so if there are multiple forms on the page then you just need to target the appropriate input fields. From there, the submit action will work out what it needs to do.&lt;/p&gt;

&lt;p&gt;Next is to define a detail block, so we point out the XPath to the heading/link for each book on the page and say we want to navigate to that page and extract the title, price, saving, etc. I&amp;#8217;ve been a little cheeky with the isbn definition, saying just find me any LI tag that contains the string &amp;#8220;ISBN-10:&amp;#8221;. If we were to look at the results generated you&amp;#8217;d see:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;puts @extractor.results.inspect
[{:book=&amp;gt;[{:title=&amp;gt;&amp;quot;The Ruby Programming Language [ILLUSTRATED]  (Paperback)&amp;quot;}, {:price=&amp;gt;&amp;quot;$26.39&amp;quot;}, 
          {:saving=&amp;gt;&amp;quot;$13.60\n      (34%)\n    &amp;quot;}, 
          {:isbn=&amp;gt;&amp;quot;ISBN-10: 0596516177&amp;quot;}]}, 
 {:book=&amp;gt;[{:title=&amp;gt;&amp;quot;The Ruby Programming Language (Paperback)&amp;quot;}, 
          {:price=&amp;gt;nil}, 
          {:saving=&amp;gt;nil}, 
          {:isbn=&amp;gt;&amp;quot;ISBN-10: 020171096X&amp;quot;}]}, 
 {:book=&amp;gt;[{:title=&amp;gt;&amp;quot;Beginning Ruby: From Novice to Professional (Beginning from Novice to Professional) (Paperback)&amp;quot;}, 
          {:price=&amp;gt;&amp;quot;$26.39&amp;quot;}, 
          {:saving=&amp;gt;&amp;quot;$13.60\n      (34%)\n    &amp;quot;}, 
          {:isbn=&amp;gt;&amp;quot;ISBN-10: 1590597664&amp;quot;}]}, 
 {:book=&amp;gt;[{:title=&amp;gt;&amp;quot;Beginning Ruby: From Novice to Professional (Kindle Edition)&amp;quot;}, 
          {:price=&amp;gt;nil}, 
          {:saving=&amp;gt;nil}, 
          {:isbn=&amp;gt;nil}]}, 
 {:book=&amp;gt;[{:title=&amp;gt;&amp;quot;Programming Ruby: The Pragmatic Programmers' Guide, Second Edition [ILLUSTRATED]  (Paperback)&amp;quot;}, 
          {:price=&amp;gt;&amp;quot;$29.67&amp;quot;}, 
          {:saving=&amp;gt;&amp;quot;$15.28\n      (34%)\n    &amp;quot;}, 
          {:isbn=&amp;gt;&amp;quot;ISBN-10: 0974514055&amp;quot;}]}, 
 {:book=&amp;gt;[{:title=&amp;gt;&amp;quot;The Ruby Way, Second Edition: Solutions and Techniques in Ruby Programming (2nd Edition) (Addison-Wesley Professional Ruby Series) (Paperback)&amp;quot;}, 
          {:price=&amp;gt;&amp;quot;$29.69&amp;quot;}, 
          {:saving=&amp;gt;&amp;quot;$15.30\n      (34%)\n    &amp;quot;}, 
          {:isbn=&amp;gt;&amp;quot;ISBN-10: 0672328844&amp;quot;}]}, 
 {:book=&amp;gt;[{:title=&amp;gt;&amp;quot;Ruby Way, The: Solutions and Techniques in Ruby Programming (Kindle Edition)&amp;quot;}, 
          {:price=&amp;gt;nil}, 
          {:saving=&amp;gt;nil}, 
          {:isbn=&amp;gt;nil}]}, 
 {:book=&amp;gt;[{:title=&amp;gt;&amp;quot;Learning Ruby [ILLUSTRATED]  (Paperback)&amp;quot;}, 
          {:price=&amp;gt;&amp;quot;$23.09&amp;quot;}, 
          {:saving=&amp;gt;&amp;quot;$11.90\n      (34%)\n    &amp;quot;}, 
          {:isbn=&amp;gt;&amp;quot;ISBN-10: 0596529864&amp;quot;}]}
]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And we have a reasonable snapshot of the data. You&amp;#8217;ll see there though, that it&amp;#8217;s not perfect. Firstly, we are missing information for some results. We&amp;#8217;ve got spaces and carriage returns in the saving data, and we probably don&amp;#8217;t need the &amp;#8220;ISBN-10:&amp;#8221; string at the front of the ISBN result. And what if we wanted to link to the actual result so someone could actually buy the book on Amazon? &lt;/p&gt;

&lt;p&gt;All good questions, and all easily solvable. I&amp;#8217;ll follow it up with a post in the next day or so and highlight some of the new ways of specifying constraints on your data in scRUBYt!. And special thanks have to go to Homeflow who have been funding at least a day of time for Peter and I each week lately, hence the increased level of development in scRUBYt!. It&amp;#8217;s nice when you have clients who want to actively give back.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/rubypond/~4/F3mx_D_x6Yw" height="1" width="1"/&gt;</description>
          <pubDate>Tue, 13 Jan 2009 01:20:13 GMT</pubDate>
          <guid isPermaLink="false">http://rubypond.com/articles/2009/01/13/more-web-scrapers-with-the-upcoming-scrubyt/</guid>
          <link>http://feedproxy.google.com/~r/rubypond/~3/F3mx_D_x6Yw/</link>
        <feedburner:origLink>http://rubypond.com/articles/2009/01/13/more-web-scrapers-with-the-upcoming-scrubyt/</feedburner:origLink></item>
    
    
  </channel>
</rss>
