• Living on the edge (of Rails) #4 - faster routes, easier form partials

    It's time again for your weekly dose of what's new in edge Rails. This weeks' report covers changes from 14 Jan 2008 to 20 Jan 2008 (the day the Rails Envy podcast was recorded).

    Route recognition is faster

    Rails' route recognition has been optimized and is significantly faster especially for applications using many resources (i.e. via map.resources in your config/routes.rb). Big thumbs up to Oleg Andreev (aka oleganza) who also wrote a detailed post about it. You can also find out more about how this works in the code comments.

    Related changesets: http://dev.rubyonrails.org/changeset/8674 and http://dev.rubyonrails.org/changeset/8652.

    render :partial and forms

    Instead of doing:

    <% form_for(:person) do |f| %>
      <%= render :partial => 'form', :locals => { :form => f } %>
    <% end %>

    you can now do:

    <% form_for(:person) do |f| %>
      <%= render :partial => f %>
    <% end %>

    Convenient. This also works with your custom form builders:

    <% form_for(:person, :builder => LabellingFormBuilder) do |f| %>
      <%= render :partial => f %>
    <% end %>

    The partial template rendered is then people/_labelling_form and the local variable in the partial is labelling_form.

    Related changeset: http://dev.rubyonrails.org/changeset/8646.

    Callbacks refactored into ActiveSupport::Callbacks

    All that duplicated callback code in ActiveRecord, ActionController::Dispatcher, and in test case setup and teardown methods have been extracted into the ActiveSupport::Callbacks module.

    Less duplication means more robust code so yay!

    Related changeset: http://dev.rubyonrails.org/changeset/8664.

    ActiveRecord test suite gets a makeover

    The ActiveRecord test suite has become something of a maze jungle in recent times, with no clear hierarchy of how things are organize. It's not that easy to find what you are looking for especially for newcomers trying to contribute to ActiveRecord. Thankfully, someone (John Barnette - the foxy fixtures guy) has stepped up and cleaned things up quite a bit. Fixtures, test cases, fixture models, etc. are now in their own directories and from looking at the directory hierarchy one should be able to know where to put what at a glance.

    Related ticket: http://dev.rubyonrails.org/ticket/10742.

    Bugfixes

    * redirect_to nil no longer explodes (but raises a friendlier 'cannot redirect to nil' exception) (related changeset: http://dev.rubyonrails.org/changeset/8633).

  • Seen in the Rails source - Orcl nds shrt indx nms

    Oracle needs short indexes


    Hah.

  • Living on the Edge (of Rails) #3 - X-Sendfile and many other sexy enhancements

    Edge Rails saw a barrage of refinements and enhancements this week and there's even talk about Rails 2.1 being just a little around the corner. There's also been a flurry of contributions to making Rails more thread-safe and performance optimizations (all still work in progress at the moment) - it's really nice to see how the Rails community is looking seriously at performance post-Rails 2.0 (not that they weren't serious about performance before Rails 2.0!).

    This week’s report covers changes from 7 Jan 2008 to the day the corresponding Rails Envy podcast was recorded (13 Jan 2008).

    X-Sendfile gets easier with send_file :x_sendfile => true

    If you only have a vague idea of what it is (like, X-Sendfile is that thing that lets you send files, right?), you'd do well to read this helpful explanation. (In short, X-Sendfile allows you to send static files directly and more performantly to HTTP clients and bypassing your app server process.) You don't have to set those headers yourself or install the plugin mentioned in that article though, since edge Rails has an augmented send_file helper that does all the legwork for you.

    All you have to do now is to this:

    send_file '/path/to.png', :x_sendfile => true, :type => 'image/png'

    Rails will set the X-Sendfile header for you (i.e. response.headers['X-Sendfile'] = '/path/to.png') and sends a HEAD response (empty response body) with the file path, allowing your web server (Apache, Lighttpd) to serve the static file and not involve your Rails processes (such as your precious, perpertually resource-starved mongrels).

    This is really nice if you are serving lots of static files (downloadable images and documents come to mind) and are not already using X-Sendfile or doing it the "long" way (by setting the response header and then render :nothing => true).

    Related changeset: http://dev.rubyonrails.org/changeset/8628

    ActionController::Base.asset_host proc now takes the request object as an optional 2nd argument

    Rails 2.0.2 allowed you to set ActionController::Base.asset_host to a proc that took a single source argument (as detailed in my earlier post). People were still running into problems with asset hosting though, particularly while trying to serve assets from an SSL-protected page (i.e. HTTPS requests). When serving SSL-protected pages, you'd need to either have an SSL certificate for each asset host(!) or live with a mixed media warning (which browsers report when you have SSL and non-SSL content on the same page).

    The asset_host proc now takes the entire controller request instance as an optional second argument. This allows you to either use a single asset host or to disable asset hosting for SSL requests. For example, this proc below practically disables asset hosting:

    ActionController::Base.asset_host = Proc.new { |source, request| 
      if request.ssl? 
        "#{request.protocol}#{request.host_with_port}" # Disable asset hosting.
      else 
        "#{request.protocol}assets.example.com" # Use asset host.
      end 
    }

    Related changeset: http://dev.rubyonrails.org/changeset/8578

    Nicer way to access request headers

    There's now a nicer way to access headers - instead of request.env["HTTP_CONTENT_TYPE"] you can now do request.headers["Content-Type"] (or request.headers["content-type"] (note all lowercase) or even good old request.headers["HTTP_CONTENT_TYPE"]. How convenient - I often worry about getting the casing right (was it 'Content-Type' or 'Content-type'?) and now I can be sure it doesn't matter!

    Related changeset: http://dev.rubyonrails.org/changeset/8625

    ActiveSupport::TestCase and friends now support declarative setup and teardown callbacks

    ActiveSupport::TestCase (and the ActionController::TestCase and ActionMailer::TestCase subclasses) now support declarative setup and teardown callbacks that are called before setup/after teardown. For example,

    class FooTestCase < ActiveSupport::TestCase
      setup :run_this_first, :then_run_this do
        # Run this last.
      end
      teardown :remove_tmp_files, :undef_constants
    
      def run_this_first
      end
    
      def then_run_this
      end
    
      def remove_tmp_files
      end
    
      def undef_constants
      end
    end

    will run run_this_first, then_run_this, and finally the stuff that's in the given block to 'setup' before running the actual setup method. The teardown callbacks are done in reverse order, meaning undef_constants is called, and then remove_tmp_files (and then the actual teardown method).

    Related changeset: http://dev.rubyonrails.org/changeset/8570

    TMail updated to 1.2.1

    The bundled TMail library (that ActionMailer uses) has been updated from 1.1.0 to 1.2.1. This new version currently maintained by Mikel Lindsaar promises bugfixes and greater test coverage. For more details, see https://rubyforge.org/frs/shownotes.php?group_id=4512&release_id=18049.

    Mikel has been working with Rails core to get Tmail Ruby 1.9-compatible. Good work Mikel!

    Related changeset: http://dev.rubyonrails.org/changeset/8620

    Bug fixes

    • ActionController::UrlWriter respects the relative_url_root. For those of you who don't know, the ActionController::UrlWriter module is a convenient mixin that allows you to use url_for and your named routes in arbitrary classes (such as in your mailers or in a BackgrounDRb worker).

      class SitemapWorker < BackgrounDRb::MetaWorker
        include ActionController::UrlWriter
      
        # Access your named routes and the url_for helper.
        url = page_url(:permalink => 'foo')
      end

      And now it respects your relative_url_root instead of blissfully ignoring it.

      Related changeset: http://dev.rubyonrails.org/changeset/8616

    • render :text => nil and render :text => false work properly now instead of inexplicably trying to render a file. This was a big gotcha when using render :text => some_variable where some_variable could potentially be false or nil in some cases - Obie Fernandez has a writeup on this long-standing bug. Related changeset: http://dev.rubyonrails.org/changeset/8577

    Optimizations

    There're significant performance improvements for classic fixtures with HABTM data, due to some slightly clever caching model classes, and instantiating fixtures from the model class, instead of expensive constant lookups from the name of the class.

    Related changesets: http://dev.rubyonrails.org/changeset/8560 and http://dev.rubyonrails.org/changeset/8561

  • Retrying code blocks in Ruby (on exceptions, whatever)

    I am not certain whether the ability to retry a code block when encountering exceptions was a feature available in Ruby, but I certainly couldn't find anything on that topic (what I did find were mostly about the retry keyword for iterator loops).

    Before you ask why I need this, the motivation for this was because I was getting intermittent HTTP errors (503s mostly) trying to connect to a web service. Turns out it's really easy in Ruby to implement a retryable method that does something like this:

    retryable(:tries => 5, :on => OpenURI::HTTPError) do
      open('http://example.com/flaky_api')
      # Code that mashes up stuff for your "social networking" site.
    end

    Here are the Kernel#retryable specs (pastie).

    And the code:

    # Options:
    # * :tries - Number of retries to perform. Defaults to 1.
    # * :on - The Exception on which a retry will be performed. Defaults to Exception, which retries on any Exception.
    #
    # Example
    # =======
    #   retryable(:tries => 1, :on => OpenURI::HTTPError) do
    #     # your code here
    #   end
    #
    def retryable(options = {}, &block)
      opts = { :tries => 1, :on => Exception }.merge(options)
    
      retry_exception, retries = opts[:on], opts[:tries]
    
      begin
        return yield
      rescue retry_exception
        retry if (retries -= 1) > 0
      end
    
      yield
    end

    It's not hard to implement the same for checking return values as well, i.e.

    retryable_deluxe(:tries => 5, :on => { :return => nil }) { puts "working..." }
    
    retryable_deluxe(:on => { :exception => StandardError, :return => nil }) do
      # your code here
    end

    Ruby is nice.

  • My very own X of the Year 2007

    It's about 9 days too late, but I figure it'd be fun to just throw these out and see if anyone else enjoyed the same things I did in 2007.

    PC Game of the Year

    This has definitely got to go to Call of Duty 4: Modern Warfare. The single-player campaign was short but visually impressive and amazingly realistic. It was like watching a movie in most parts. And really, sneaking by Spetsnaz just a few metres away in sniper ghillie suits and was totally pimp. Multiplayer is real good fun too, I'm still playing it now (usually on GamingSA.com servers as 'konata').

    Special mention: World of Warcraft (yes, I quit it ages ago, but it's still good for the early part of 2007 when I was still playing casually).

    PSP Game of the Year

    Yes, I got a PSP just this year. And yes, Jeanne d'Arc is the best game I've played and completed in a while. For those who don't know, Jeanne d'Arc is a very accessible Strategy RPG (SRPG) that is very loosely based on Joan of Arc. It's much easier to play than Final Fantasy: Tactics or Disgaea: Hour of Darkness and doesn't leave you constantly wondering about missing out on secrets. Highly recommended if you're looking for an RPG-like game on the PSP.

    I haven't had the chance to play many other PSP games, but look forward to getting deeper into Disgaea: Hour of Darkness (and highly anticipating Final Fantasy VII: Crisis Core in March 2008).

    Anime of the Year

    2007 is the year I switched my anime tastes from mostly shonen anime (like Naruto and Bleach) to the seinen genre.

    My favorite anime for 2007 (this is a tough one) has to go to Lucky Star. I even went through a prolonged Konata-ism phase and even had a printout of Konata saying "Relax" pasted right above where I sit in the office.

    Konata says 'Relax'


    It's therapeutic :)

    2007 was a good anime year. Tekkon Kinkreet had an edgy, surreal drawing style but had a fantastic plot. I highly recommend it if you've missed it - it's about 10 times better than your run-of-the-mill Princess Mononoke or Spirited Away type anime movies, in my humble opinion.

    Potemayo's moeblobs were just too funny to watch. Gochuko (below) had me LOL when she went around using tape to fix everything she sliced with her big scythe. At first appearances Potemayo may look like a kid's anime but it's really a work of comic genius for adults.

    Gochuko from Potemayo


    Genshiken 2 picks up where the 1st season left off and while the ending left quite a bit to be desired, on the whole it was enjoyable watching otakus cope with life (who knew it was so tough to get a job in Japan).

    What about you?

    What did you enjoy in the last year and more importantly, do you have any recommendations for an ani-otaku like me?

subscribe via RSS