Ruby is beautiful – longest word length

Sometimes it makes me smile how things can be implemented in such a simple and beautiful way in Ruby.

We needed a method to return the length of the longest word in a string. So we opened up the String class and gave it a new toy to play with:


class String
  # Returns the length of the longest word in the string
  def longest_word_length
    self.split(' ').max.length
  end
end

Are there any little methods you have written that make you smile at their simplicity?

Comparison <=> operator on string attributes of ActiveRecord objects

Here’s a little gotcha for you which caught me out by surprise, obvious when you think about it, but irritating if you miss it: the ruby String comparison operator does case-sensitive comparisons.


# Good:
['C', 'b', 'A', 'D'].sort #=> ['A', 'b', 'C', 'D']

# Bad (what <=> gives you out the box):
['C', 'b', 'A', 'D'].sort #=> ['A', 'C', 'D', 'b']

For a nice efficient implementation of a AR model comparitor use the String#casecmp method for a case-insensitive comparison:


class MyObject
  def <=>(other)
    self.my_attribute.casecmp(other.my_attribute)
  end
end

Overriding fundamental ActiveRecord methods (like destroy) whilst preserving callbacks

OK, so you’ve decided that it’s a good idea for you to override one of the fundamental methods on one of your ActiveRecord models. But in doing so, you have discovered that all of a sudden your callbacks have stopped working.

The reason for this is when your class is loaded, ActiveRecord::Callbacks have already hooked in the callback methods around the relevant methods using alias_method_chain meaning that the once named destroy method is now called destroy_without_callbacks.

So, to actually override the destroy method, use the following code:


def destroy_without_callbacks
  unless new_record?
    # do your code here
  end
  freeze
end

Note that the implementations will differ for other methods e.g. create, so you should check the ActiveRecord base class to see what should be in there.

Does your rabbit tell you about your build?

A rabbit is for life…

A French company called Violet sells a number of “Things” – internet-enabled devices that interact with you and/or your environment.  (Actually you can buy them from lots of resellers).  They include mirrors, stamps, lamps and strangely rabbits!  The first generation rabbit was called Nabaztag (Nabaztag is Armenian for “rabbit”), whilst the second  generation is called a Nabaztag:tag, and amongst other enhancements, has been blessed with the ability to “sniff” RFID tags and play MP3 files.  You can identify a Nabaztag:tag by it’s belly-button microphone which enables the rabbit to hear a small number of single word commands, e.g. “weather” and respond, e.g. by telling you what the weather will be.  Wikipedia can tell you more about Nabatztags.

I recently got a Nabaztag:tag.  He (or is it a she?) comes in clean pure white, although Violet also sells coloured replacement ears, and many people customise their rabbit’s look.  Of course, the geek in me didn’t care too much about style and rather wanted to fiddle with how it works and get it to do different things…

Cruisecontrol.rb is for peace of mind…

As was alluded to in a previous blog post, we use CruiseControl.rb to do continuous integration of our code.  Within ten minutes of checking some code into Subversion (yes we use that too), our CruiseControl.rb server will automatically

  • check the updated code out
  • update the application’s database using Rails migrations
  • start the application
  • run our unit tests, our integration tests and our performance tests
  • do some profiling of our code
  • shut down the application again
  • build our documentation
  • and finally trawl through our code picking out the TODOs and FIXMEs etc.

This is great, but how do we know whether it worked or not?  Well, we use CCMenu which sits on our menu bars and quietly shows a coloured blob giving the current state of the build.  Some of us integrate it with Growl to actively tell us whether it succeeded or failed when the build completes.  But that wasn’t enough for me!

Tell me, tell me now!

Yep, I had to integrate CruiseControl.rb and my rabbit.  Unfortunately, CruiseControl’s documentation on writing plugins is a little sparse (I couldn’t find any truly complete documentation), and Violet’s documentation isn’t ideal either (and they keep moving it although there is some general consensus between them all).

So, I’ve tried to simplify it for you.  First I wrote a CruiseControl.rb plugin using the sparse documentation and got it working, but it didn’t do much because I’d only learnt about a couple of hooks.  Thinking that there must be more, I trawled the CruiseControl.rb source code to discover all the possible hooks… and there were lots more!  So I added methods for all of the possible CruiseControlRB hooks to my plugin.

Finally, I used that complete template plugin to integrate with my rabbit.  And here’s the code:


#!/usr/bin/ruby

require 'net/http'
require 'uri'

class NabaztagNotifier

  # Configure your Nabaztag's details here.
  NABAZTAG_SERIAL_NUMBER = 'xxxxxxxxxxxx'
  NABAZTAG_TOKEN = 'yyyyyyyyyy'

  COLOURS = {
    :black    => "0,0,0",
    :red      => "255,0,0",
    :green    => "0,255,0",
    :blue     => "0,0,255",
    :magenta  => "255,0,255",
    :cyan     => "0,255,255",
    :yellow   => "255,255,0",
    :white    => "255,255,255",
  }

  def initialize(project)
#    puts "initialize"
  end

  def no_new_revisions_detected
#    puts "no_new_revisions_detected"
  end

  def new_revisions_detected(revisions)
#    puts "new_revisions_detected"
  end

  def sleeping
#    puts "sleeping"
  end

  def build_requested
#    puts "build_requested"
  end

  def configuration_modified
#    puts "configuration_modified"
  end

  def build_initiated
#    puts "build_initiated"
  end

  def build_started(build)
#    puts "build_started"
    rabbit(:colour => :yellow, :message => "Build started for #{committer(build)}", :status => :building)
  end

  def build_finished(build)
#    puts "build_finished"
    if build.successful?
      rabbit(:colour => :green, :message => "Build fixed by #{committer(build)}", :status => :success)
    elsif build.failed?
      rabbit(:colour => :red, :message => "Build broken by #{committer(build)}", :status => :failure)
    elsif build.incomplete?
      rabbit(:colour => :yellow, :message => "Build incomplete", :status => :incomplete)
    else
      rabbit(:colour => :yellow, :message => "Build status unknown", :status => :unknown)
    end
  end

  def build_loop_failed(error)
#    puts "Build loop failed: #{error.class}: #{error.message}"
    rabbit(:colour => :red, :message => "Build failed with an error", :status => :failure)
  end

  def build_broken(build, previous_build)
#    puts "build_broken"
#    rabbit(:colour => :red, :message => "Build broken by #{committer(build)}", :status => :failure)
  end

  def build_fixed(build, previous_build)
#    puts "build_fixed"
#    rabbit(:colour => :green, :message => "Build fixed by #{committer(build)}", :status => :success)
  end

  private

  #
  def committer(build)
    match = build.changeset.match(/ committed by (\w+) /)
    if match.nil?
      return "unknown"
    elsif match[1].length > 1
      return "#{match[1][0,1].upcase} #{match[1][1..-1].capitalize}"
    else
      return match[1]
    end
  end

  def rabbit(options)
    url = "http://api.nabaztag.com/vl/FR/api.jsp?sn=#{NABAZTAG_SERIAL_NUMBER}&token=#{NABAZTAG_TOKEN}"

    # Quarter second tempo
    url += "&chor=4"

    # Ears up => success
    # Ears down => failure
    # Ears askew => unknown
    if options.has_key?(:status)
      angle = case options[:status]
        when :success
          [0, 0]
        when :failure
          [180, 180]
        else
          [90, 90]
        end
      angle.each_index do |ear|
        url += ",0,motor,#{ear},#{angle[ear]},0,#{ear}"
      end
    end

    # Colour:
    #   Symbol => named colour
    #   String => "R,G,B"  (0-255 in each value)
    if options.has_key?(:colour)
      colour = case options[:colour]
      when nil
        COLOURS[:blue]
      when Symbol
        COLOURS[options[:colour]]
      when String
        options[:colour]
      else
        COLOURS[:white]
      end

      (0..12).each do |n|
        url += ",#{n    },led,#{n % 4 + 1},#{colour}"
        url += ",#{n + 1},led,#{n % 4 + 1},#{COLOURS[:black]}"
      end
      url += ",14,led,1,#{colour},14,led,2,#{colour},14,led,3,#{colour}"
    end

    # Message
    url += "&tts=#{URI.escape(options[:message])}&ttlive=10" if options[:message]

    puts "Sending Nabaztag: #{url}"
    $stdout.flush
    Net::HTTP.get_print URI.parse(url)
  end

  public

  def test
    puts "testing"
    rabbit(:colour => :blue, :message => "Boo", :status => :abc)
    build_started(nil)
  end

end

# Uncomment the following line to test:
# NabaztagNotifier.new(nil).test

Project.plugin :nabaztag_notifier

As it stands, it is a drop in CruiseControl.rb/Nabaztag:tag plugin that announces the start of a build and who started it, and the completion of a build and who broke it or fixed it.  However, it is ripe for extension to make it do what you need – make the rabbit more chatty, or rip out the Nabaztag bits and reuse it as the starting place for a standard CruiseControl.rb plugin.

Installing it

There are 3 steps to installing the Nabaztag:tag CruiseControl.rb plugin.

  1. Installing the plugin depends somewhat on your CruiseControl.rb version.  So far we’ve used two different versions:
    • in the first you had to put the above file in builder_plugins/installed directory in CruiseControl.rb, making sure that the file has the right owner and permissions.
    • in our current CruiseControl.rb version, you need to put it in the builder_plugins directory, again making sure that the file has the right owner and permissions.
  2. Then, you’ll need to edit the first few lines to set the
        NABAZTAG_SERIAL_NUMBER = 'xxxxxxxxxxxx'
        NABAZTAG_TOKEN = 'yyyyyyyyyy'

    You can get the serial number from your rabbit – follow the Violet instructions for registering. Once you have registered with Violet and opened your violet.net account, then you can configure your rabbit to join the “Violet ecosystem”, i.e. generate the token.  Now you have the serial number and the token, update the appropriate lines in the plugin.

  3. Finally, simply restart CruiseControl.rb to pick up the plugin.

To test that it is working, start a build – if you start one from CruiseControl.rb’s web interface, the rabbit should announce “Build started for unknown” whilst flashing yellow LEDs and cocking his ears forward expectantly.  Later, hopefully your build will succeed and the rabbit will announce “Build fixed by unknown”, flash green LEDs and perk his ears up, but if it fails then the rabbit will announce “Build broken by unknown”, flash red LEDs and put his ears down flat.

How to tweak it to suit you

Out of the box, the plugin will announce the start and completion of the builds, along with the Subversion committer of the latest checkin.  To help the rabbit pronounce our usernames, the plugin splits the first character off the username, e.g. “jsmith” becomes “J Smith”.  This is done in the private committer() method – modify it to suit the pronunciation of your Subversion usernames, or even to cope with a different source code control system.

By default, the plugin uses the very boring, but universal colours of green meaning good, red meaning bad and yellow meaning warning.  Hence, the rabbit flashes yellow when announcing the start of a build, red if the build fails and green if it succeeds.  You can modify the COLOURS constant in the script to define more colours, and then refer to them in calls to the rabbit() method.  The colours are defined as red/green/blue combinations between 0 and 255.

As I just alluded to, the private rabbit() method does the hard work of putting together a message and a choreography (what Violet calls a sequence of ear movements and LED flashes).  So, this is the method you would modify if you want to change the rabbit’s behaviour.  The Violet documentation (or wherever it is today) isn’t particularly clear, but hopefully is a bit easier to follow with this example.  One thing you could do is to use the voice API parameter to use something other than the default voice.

Finally, as I mentioned earlier, the plugin includes methods for all the hooks I could find in CruiseControl.rb.  You could use any of them to make your rabbit more chatty, or to integrate CruiseControl.rb with other services such as sending emails, writing to logs, etc.

Good luck; have fun.

Avoid minification with IE conditional javascript

In order to save bandwidth, merging and minification of CSS and JS; assets is really useful.  However be wary of including any code in that which uses IE conditional javascript.  Something I did not realise existed until digging into this issue, despite many years of conditionally dealing with different browsers!  For those not in the know, in IE /*@cc_on!@*/ will “evaluate” to ! thereby making var isIE = !false; which is true, of course.  Any other browsers will ignore the comment and set var isIE = false.

Helpful until that code is run through a minification which strips all comments and is not aware of such things!

Annoyingly, code from a 3rd party that is part of our application was being minified in production mode and uses this technique to identity if a browser is IE or not.  Which gave very spurious results when testing IE…  The solution for us, rather than hacking the 3rd party code was to use a direct include of the file into the pages that require it, and to use ?v=x.x.x after the filename in the source to ensure we can still force reloads in clients after we make changes to it (probably just an upgrade) in future.  This was something the merging and minification process kindly gave us for free.  In this case I made the number match the version of the 3rd party source we are using for consistency.  This is a pain in that it requires changes to templates and it not automatic, but modifications to the included file in this case are likely to be rare, and is the easiest option allowing the rest of our assets to still be merged and minified.

Performance implications of procs and blocks in Ruby

Whilst profiling the performance of some code, I wondered why a method that was yielding to a small block of code was taking so long.

The answer was in the method arguments where out of habit I automatically capture the block argument and convert it to a proc using the ampersand (&) operator. When yielding to code that you are passing to a method, there is no need to convert the block to a proc as the block of code that you are yielding to is already a proc in the correct context.

An example to clarify:


# if you just want to yield to code, this way is inefficient
# as the block argument will be converted to a proc
def foo(argument, &block)
  if some_logic
    block.call  # if we are just yielding to the block in the
                  # context it is already in, this is unnecessary.
  end
end

# if you are just yielding to code, this way is better
def bar(argument)
  if some_logic
    yield  # much better, there was no overhead
            # of converting the block into a proc
  end
end

# called like so
bar(my_argument) { p "Hello" }

Blocks and procs are complicated, here are a couple of resources:

Profiling your Ruby on Rails application

At some point you want to check that there aren’t any really slow bits of your application and even if there aren’t, you might like to know where to spend effort in optimising.  Luckily for you the script/performance/request script coupled with ruby-prof gem produces very useful profiling reports.

Getting script/performance/request to work with the standard gem (version 0.6.0) is troublesome impossible, however that nice man Jeremy Kemper from 37signals has published a version (0.6.1) that does work! Hurrah!

You can just install jeremy-ruby-prof from the git gem repo, however this installs the gem with the wrong name if you want to use it with script/performance/request. It can be done by downloading the gem, building the gem and then installing it from the local gem. E.g. (on a Ubuntu box):

wget http://github.com/jeremy/ruby-prof/tarball/89e2a4bc3f5881519a2fe1e5c5c05f7e1e0acf6e
tar -xf jeremy-ruby-prof-89e2a4bc3f5881519a2fe1e5c5c05f7e1e0acf6e
cd jeremy-ruby-prof-89e2a4bc3f5881519a2fe1e5c5c05f7e1e0acf6e
rake gem
sudo gem install pkg/ruby-prof-0.6.1.gem

Ta da! Installed with the right name and now you will be able to create yourself a benchmarking environment and profiling script like the tutorial at Railscase – Request Profiling.

By default, two outputs are generated in your tmp/ directory. An HTML call graph (see Reading Call Graphs) and a flat profile (txt) file.

Writing ruby code that only executes in development mode

Our application does a lot of parsing of tree structures in development mode which aids our application developers in creating squeeky-clean code. However, in production we don’t really want and don’t need the overhead of all these additional recursive tree-parsing methods, but we also don’t want to change the application code.

The answer is to leave the application code making calls to the platform, but adapting the methods in the platform to only execute the method bodies in development mode.

We have a module that is mixed in where appropriate:


module DefineInDevelopment

  # Defines a method used to define other methods that are only available in development mode
  if ENV['RAILS_ENV'] == "development"
    def define_in_development
      yield
    end
  else
    def define_in_development
    end
  end

end

Then in any method where we only want the processing overhead in development, we wrap the method’s body in a define_in_development method call passing a block:


include DefineInDevelopment
def my_expensive_method_for_development(arg1, arg2) do
  define_in_development do
    # really expensive code
  end
end

Simple, but incredibly effective! Exploring this also identified a performance gotcha which I discuss in my next blog entry.

Using RCov with a remote server or mechanize

Here at workbooks, we use a number of different testing techniques including server-based unit and rspec testing and client-based selenium testing.

To power our ExtJS interface, requests to our RESTFul API with a specific extension reply with an ExtJS compatible JSON object. In order to test if the JSON coming back is what we expect, we wrote a test helper for rspec which uses mechanize to make HTTP requests and a JSON parser to analyse the response against known key/value pairs.

Like many, we wanted to be able to determine the effectiveness of our tests in various areas of our code base and also integrate this into our continuous integration system so we have a constant indicator of whether we are testing new code. The best way to do this is using RCov.

We soon found that when we ran our rspec tests using RCov, that the code paths were not being marked as executed. After a little thinking; we realised that our rspec tests were not invoking the ruby code directly because of our mechanize implementation. A quick google search later revealed that you can also run script/server through rcov – an ideal solution:


rcov script/server -o log/coverage --rails

We modified our cruise control rake task to run up the server using the snippet above and ensured that the coverage report gets put in the log directory (as our custom build artifacts are published from the contents of the log directory). The end result being, that for every build; we now have a rcov report for our tests.

It’s also worth noting that we use the report aggregation feature of rcov (not shown in the example for clarity), as the server is only started for the mechanize/client-side based tests. Model and controller unit testing etc is run without a need for the server.

Undefined local variable or method ` ‘

Ever had this little nugget of information? Lets play spot the difference…

Example 1
def my_method(args) # :nodoc:

Example 2
def my_method(args) # :nodoc:

You will notice that there is no apparent difference, which is why I couldn’t work out why one line of code ran, and one caused an error. Anyway, to cut a long story short, I found a unicode character on the line that wasn’t working – and that unicode character didn’t have a corresponding variable or method!

So, Chris’ Thursday Tip is to turn on invisible characters in TextMate (View -> Show Invisibles ⌥⌘I).