RSpec 1.0.0 Finally Here!

Written on 3:32:00 PM by S. Potter

Thanks to the RSpec team for finally delivering RSpec, which I have been excited about for quite some time. Having about 5 projects (2 pure Ruby, 3 Rails) using RSpec 0.8.2 and 2 other projects still on (0.7.5) I was a little worried about migrating to the new RSpec 1.0.0 compliant API, which is a fair bit different than some of what was allowed in 0.8.2 and prior. However, the RSpec developers in all their wisdom foresaw this problem and wrote spec_translate where you simply give it the from directory and to directory in that order on the command line. Now it doesn't do everything, so don't expect too much, but it will convert all the context, setup, specify and teardown blocks to describe, before, it and after blocks respectively with very little fanfare or effort required from us. The gotchas for those of us that ignored the Test::Unit Cheat Sheet on the website for recommended RSpec usage (I am going to own up to it, yes):

  1. .should.eql expected_value becomes .should eql(expected_value)
  2. .should.not... becomes .should not...
  3. .should.be expected_result becomes .should be(expected_result)
Let me know if you find different varieties of the above API differences.

Are you a Pig or a Chicken?

Written on 8:06:00 PM by S. Potter

Grey's Anatomy fans amongst us may remember an episode last season (season two) where a patient's boyfriend introduced us to the idea of whether you are a Pig or a Chicken in a relationship. The idea is that when you have a nice greasy breakfast of bacon and eggs, you have the Pig who is completely committed to delivering the breakfast and the Chicken who is only involved in contributing to the breakfast. So in the Grey's Anatomy episode the boyfriend was trying to be a Pig in his relationship and be fully committed to his heavily pregnant girlfriend. For me this wasn't a new concept, having read about Scrum as a software project management practice or adaptive process (if we are really forced to use the word "process") over the last 3 years. Now in Scrum the idea is the same, except applied to the software project not a relationship between two people. The Pigs are generally the people who are 100% committed to the project/product (e.g. Developers, DBAs, BAs, QAs, etc.) and the Chickens are the people that have a stake in the project but only contribute to the project. They are not 100% committed (e.g. business stakeholders, management, etc.) just involved in the project. Are you a Pig or a Chicken? Pig and Chicken cartoon

Ruby Equality

Written on 12:48:00 PM by S. Potter

Since two people asked me in the space of 5 days about this, I thought I'd post a quick demystification of Ruby's "equality" facilities. Namely equal?, ==, eql? and ===.

  1. equal? - always (or should be) purely identity equality, and *should never* be overridden by subclasses
  2. == - by default is object identity equality, but *can* be overridden by subclasses
  3. eql? - is object value equality and *should* be overridden by subclasses
  4. === - by default same as #==. It is overridden by subclasses that need a "case" equality defined to make sense when used in the case statements in a when construct.

ActiveRecord::Base#to_xml woes

Written on 11:10:00 PM by S. Potter

For us Railies that use namespaced models inside Ruby modules and are relying on ActiveRecord::Base#to_xml, do not fret any longer. To reproduce the problem you just need to do something like: users = Legacy::User.find(:all) #=> [] users.to_xml #=> <?xml version=\"1.0\" encoding=\"UTF-8\"?><legacy/users></legacy/users> The XML produced by #to_xml is NOT valid XML for this case. It's been a small thorn in my side for a week or so now and I've been living with it by adding the following to a file that is eventually/implicitly required by environment.rb:

# Patch for Rails #8305 that I submitted on 2007-05-09
# We'll need to keep an eye out upon next Rails Gem upgrade whether or not this 
# fix has been applied to remove this or not.
require 'active_record'
require 'active_support'

class ActiveRecord::Base
  alias :old_to_xml :to_xml
  def to_xml(options = {}, &block)
    options[:root] ||= self.class.name.tableize.singularize.gsub(/\//, ':') # for models that are namespaced in Ruby module
    old_to_xml(options, &block)
  end
end

module ContainerPatchMixin
  def self.included(base)
    base.class_eval %{
      alias :old_to_xml :to_xml
    }
    base.send(:include, InstanceMethods)
  end
  
  module InstanceMethods
    def to_xml(options = {})
      contained_class = self.first.class.name
      options[:root] ||= contained_class.tableize.gsub(/\//, ':') # for models that are namespaced in Ruby module
      options[:children] ||= contained_class.tableize.singularize.gsub(/\//, ':') # for models that are namespaced in Ruby module
      old_to_xml(options)
    end
  end
end

class Array
  include ContainerPatchMixin
end
Now what is produced is something more along the lines of: <?xml version=\"1.0\" encoding=\"UTF-8\"?><legacy:users></legacy:users> Which is valid XML. Today I finally reported this edge case defect on dev.rubyonrails.org (#8305) as I hadn't seen anyone else report it yet. Thankfully this is Ruby and this is all possible while the Rails Core team determine if my not so elegant fix is worthy to be in the next Rails version or not.

Twitter4R 0.1.0 Released

Written on 11:55:00 PM by S. Potter

This weekend I had a couple of hours to work on Twitter4R (finally). Today (Sunday) saw the release of Twitter4R 0.1.0 on Rubyforge as a Gem. It may take the Rubyforge gem mirrors a few hours to copy it over, but by Monday morning (US time) you should be able to do the following: $ sudo gem install twitter4r $ irb irb(main):001:0> require 'twitter' => true irb(main):002:0> client = Twitter::Client.new :login => 'screen_name', :password => 'password' => # irb(main):003:0> statuses = client.public_timeline => [#, ...] irb(main):004:0> statuses.each do |status| irb(main):005:1* puts status.text, status.user.screen_name irb(main):006:1> end the puts output would go here.... irb(main):007:0> client.update 'Pure Ruby API for Twitter: http://twitter4r.rubyforge.org' => nil It supports utf8 strings so this should not be a worry when querying the public timeline! See Twitter4R Rubyforge Website for more details or the Twitter4R Project Blog. Enjoy!

Favorite Quote of the Week

Written on 11:29:00 AM by S. Potter

A friend and old co-worker, who wants to be referred to as Bo "Fucking" Conroy, just sent me this on IM:

yeah, php seems like something targeted at 16-22 year old porn site developers.
Thanks Bo! Only you can verbalize these delicate things so precisely.

Rails Application Scaling Tips

Written on 9:34:00 AM by S. Potter

This will be a howto on quickly enhancing performance, throughput or uptime of a Rails application:

  • Use memcached for sessions
  • Move static assets to separate lighttpd or httpd web server optimized for just static content
  • Use more efficient templating engine such as erubis
  • Use memcached for other high-frequency read AR models
  • Configure Rails to cache pages, actions and fragments where relevant
  • Use BackgrounDRB to delegate longer running tasks such as sending emails, etc. Note: This is out of date right now, I might recommend Cloud Crowd or other plugins at this point for background jobs or even using message oriented middleware (MOMs) like AMQP services like RabbitMQ.
  • If your application requires a large number of concurrent large file uploads (say you are building the next YouTube) you should consider utilizing Merb. Note: this is also out of date since I wrote it in May 2007.
At this point you just need to focus on the standard scaling concerns:
  • database server tweaking (including database caching)
  • removing unnecessary file system I/O
  • removing unnecessary or consolidating network I/O
  • Removing unnecessary computation or caching computational results where necessary
  • etc.

Update (2010-08-15)

The following tools/plugins can be used to optimize the how many and how much data is passed by the SQL queries generated by ActiveRecord:

  • Bullet - helps you optimize your SQL queries by notifying you when not eager loading or using counter caches.
  • Scrooge - SQL query optimizer which can reduce the amount of data getting sent from your database to your Rails application.
  • Rack-bug - reports stats about each request in development mode so you can see how many queries are being executed on each request (and a number of other useful request stats).

For identifying memory bloat or leaks in the applications the following might be useful (when we have a need for such tools):

  • Memorylogic - logging memory usage.
  • Oink - for easily finding actions that significantly increase VM heap size.
  • BleakHouse - used this before for finding memory leaks in a Rails 1.2.3 app a couple of years back. I believe it is working against Rails 2.3.4 apps now too.

Also nesting includes on eager loading in ActiveRecord used to have memory leak implications. I wrote about my experience in 2007 here: Twas the night before launch.


The memory leak from the Twas the night before launch blog post may have been fixed in subsequent releases of ActiveRecord since July 2007 when I encountered it, in the case of nesting includes for eager loading in all one query, but the morale of the story is never assume that just because you are eager loading all in one query there aren't going to be other implications.