Initial reactions to Rails 1.2... annoyed

Posted over 7 years back at Wood for the Trees

I’ve held off my opinion about Rails 1.2 until I had a little more experience with it. First of all, I’m going to say I’m glad Rails has put out this release, because some of the features in Edge I’ve been needing for a while and, in cases where I couldn’t wait, like my simply_restful_backport, I hacked a ported version to 1.1.x. I also had unpublished ports for REST, routing, and a few deprecations to prepare myself. Most of the added features are bang on what is needed when following the Rails methodology of web development: singleton resources, nesting resources, the emphasis on REST, the respond_to(&block), etc.

But my optimism and preparations were in vain. I’m annoyed. My initial reaction is that Rails 1.2 is a fascist. The front-facing libraries, ActiveRecord and ActionPack, are not the cause of my annoyance. It’s the underbelly, ActiveSupport, which has acquired a rank smell.

How Deprecations are handled

I’ll be brief. Backward compatibility is beautiful and when you can’t maintain it, you prepare developers for the transition. Deprecrations are usually the answer: you say it is going to be removed for a long time, encourage people away from it, then finally remove it in either the next major release or the one after that, safe in the knowledge that everyone who will be affected by the change has got the message.

Rails has not held to the time-honoured traditions of deprecation. It does one of three things: fill up the logs with warning messages, raise annoying exceptions that apologise something is gone, or just plain crash because the old API had changed so much. That’s not deprecation. That’s just API upheaval.

And it’s unacceptable to have so many differences in the framework which, first, are undocumented, and second, change development so much. I would expect this behaviour from a 1.3 to 2.0 upgrade, but not between two minor releases. Maybe Rails Core should have re-numbered the release to warn users that Rails has changed quite drastically under the hood. The changes are fine, but it’s the way they came unannounced that is annoying.

Especially after so much time between the two minor versions, there was ample opportunity to tell developers… 1.1.6 was the last opportunity, and a better time was back at 1.1.2, when these additions already existed or were being considered. That is still too quick, but to give no warning, not even a proviso ‘this may change’, until it is too late is just insufferable.

ActiveSupport::Dependencies < ActiveSupport::Devil

For example, I don’t know why an innocuous ObjectSpace block in routes.rb should cause a blocking exception from ActiveSupport::Dependencies. It prevented my application from even loading and took half a day to track down. Or why I needed to alter ExceptionNotifier to require all its libs. Or track down 6 other ‘dependencies’ and make them explicit—I thought Rails was supposed to make this stuff easier, not more of a pain than C’s #include <hardtofind.h>?

It’s ridiculous for an ‘improvement’ release to be so fragile. Rails 1.1.x was pretty flexible on these terms. However, Rails, in its growing focus on encouraging programmers into standardised practices, is looking more like 37signals practices. And to say they are ‘best practices’ or good ‘conventions’ is a bit rich, because Rails is not the cleanest set of libraries. Just run it with $VERBOSE = true and you’ll see what I mean.[1]

In fact, after using ActiveSupport::Dependencies for a while, I’ve come to the conclusion that it is too clever for its own good and I foresee lots of bugs in Rails Core Trac about it. It’s too prying. It’s too observant of everything that is happening. It infects the production environment. It’s a glorified const_set trace_func.

ActiveSupport.include :bloat

What kind of overhead does ActiveSupport::Dependencies have? How many orders of magnitude slower is it than the previous Reloadable module, which just included a method, reloadable?, and was found via ObjectSpace? First of all, it didn’t need a revolution like Dependencies; it was clean, simple, elegant and extendable. It didn’t solve all the problems and had room for improvement, but removing it I think was a mistake. ActiveSupport::Dependencies, unlike it’s predecessor, is a beast. Here’s a quick LOC comparison between activesupport 1.3.1 and 1.4.1 (ignoring hooks in the other gems):

$ grep -v '^\s*#' activesupport-1.3.1/lib/active_support/reloadable.rb | wc -l
28
$ grep -v '^\s*#' activesupport-1.3.1/lib/active_support/dependencies.rb | wc -l
172

$ grep -v '^\s*#' activesupport-1.4.1/lib/active_support/reloadable.rb | wc -l
56
$ grep -v '^\s*#' activesupport-1.4.1/lib/active_support/dependencies.rb | wc -l
538

Not the most reliable comparison, but it gives a good idea of the bloat introduced by, first, dependency tracking and, second, simple deprecating. Most of those lines of code in Reloadable are about how it is not just deprecated, but downright crippled and how evil it was. This is not a good practice for a developing an open source framework which is not easy to upgrade (and we live with it, because Rails is useful). The difficulty of upgrading is precisely because of these badly planned deprecations and backward incompatibilities.

Enough ranting

In this sense, Rails 1.2 is by the far worst gem update I’ve experienced so far. How many blogs have posted about the many little things that needed to be changed to upgrade? How many forward-thinking developers looked in cringing, abject horror when their tests failed left and right after that (apparently) over-optimistic gem update rails? How many people flew to IRC and the mailing lists for help? Let me be so conceited as to introduce a ‘best practice’: discuss major changes with your users.

I realise that Rails is first and foremost a 37signals project, evolved from DHH’s work there and is primarily altered and improved for that company’s purposes, but as an open source project trying to get more users, it’s important to either branch those changes which are special to that company or to be more considerate to the user base. I think Rails development would benefit from:

  • a separate experimental branch (different from Edge trunk) for API changes—Radiant does this quite nicely
  • a highly visible discussion board on rubyonrails.org to discuss major changes with a link to it in IRC and regular updates to the Rails mailing lists

Granted, much of my qualms with Rails 1.2 is with Dependencies and with some good old fashioned hacking, they can fix it. However, I think it and the library from which it has spawned are worth my comment. Dependencies is the most invasive addition to the framework I’ve seen to-date. I appreciate the attempt at innovation, but Rails needs to stop doing this kind of widespread deprecation and fundamentalist intolerance to the unconventional.

I wish the development team would take more pleasure in the diversity of uses of Rails and less perverse pleasure in the militant drilling of young, transitional and experienced programmers. I like the framework, but I don’t work (or want to work) at 37signals.

1 I’ve been aware of my own transgressions for a while. I’m in complete agreement with Mauricio on the topic of warnings. They are there for a reason: to create semantically correct Ruby. We should all be able to run our fantastically short, readable, meta-programmed, overloaded and mixin-loving code with $VERBOSE = true. It isn’t difficult to achieve and it helps in the long-run to make that lovely little code more readable, usable and quiet.

Initial reactions to Rails 1.2... annoyed

Posted over 7 years back at Wood for the Trees

I’ve held off my opinion about Rails 1.2 until I had a little more experience with it. First of all, I’m going to say I’m glad Rails has put out this release, because some of the features in Edge I’ve been needing for a while and, in cases where I couldn’t wait, like my simply_restful_backport, I hacked a ported version to 1.1.x. I also had unpublished ports for REST, routing, and a few deprecations to prepare myself. Most of the added features are bang on what is needed when following the Rails methodology of web development: singleton resources, nesting resources, the emphasis on REST, the respond_to(&block), etc.

But my optimism and preparations were in vain. I’m annoyed. My initial reaction is that Rails 1.2 is a fascist. The front-facing libraries, ActiveRecord and ActionPack, are not the cause of my annoyance. It’s the underbelly, ActiveSupport, which has acquired a rank smell.

How Deprecations are handled

I’ll be brief. Backward compatibility is beautiful and when you can’t maintain it, you prepare developers for the transition. Deprecrations are usually the answer: you say it is going to be removed for a long time, encourage people away from it, then finally remove it in either the next major release or the one after that, safe in the knowledge that everyone who will be affected by the change has got the message.

Rails has not held to the time-honoured traditions of deprecation. It does one of three things: fill up the logs with warning messages, raise annoying exceptions that apologise something is gone, or just plain crash because the old API had changed so much. That’s not deprecation. That’s just API upheaval.

And it’s unacceptable to have so many differences in the framework which, first, are undocumented, and second, change development so much. I would expect this behaviour from a 1.3 to 2.0 upgrade, but not between two minor releases. Maybe Rails Core should have re-numbered the release to warn users that Rails has changed quite drastically under the hood. The changes are fine, but it’s the way they came unannounced that is annoying.

Especially after so much time between the two minor versions, there was ample opportunity to tell developers… 1.1.6 was the last opportunity, and a better time was back at 1.1.2, when these additions already existed or were being considered. That is still too quick, but to give no warning, not even a proviso ‘this may change’, until it is too late is just insufferable.

ActiveSupport::Dependencies < ActiveSupport::Devil

For example, I don’t know why an innocuous ObjectSpace block in routes.rb should cause a blocking exception from ActiveSupport::Dependencies. It prevented my application from even loading and took half a day to track down. Or why I needed to alter ExceptionNotifier to require all its libs. Or track down 6 other ‘dependencies’ and make them explicit—I thought Rails was supposed to make this stuff easier, not more of a pain than C’s #include <hardtofind.h>?

It’s ridiculous for an ‘improvement’ release to be so fragile. Rails 1.1.x was pretty flexible on these terms. However, Rails, in its growing focus on encouraging programmers into standardised practices, is looking more like 37signals practices. And to say they are ‘best practices’ or good ‘conventions’ is a bit rich, because Rails is not the cleanest set of libraries. Just run it with $VERBOSE = true and you’ll see what I mean.[1]

In fact, after using ActiveSupport::Dependencies for a while, I’ve come to the conclusion that it is too clever for its own good and I foresee lots of bugs in Rails Core Trac about it. It’s too prying. It’s too observant of everything that is happening. It infects the production environment. It’s a glorified const_set trace_func.

ActiveSupport.include :bloat

What kind of overhead does ActiveSupport::Dependencies have? How many orders of magnitude slower is it than the previous Reloadable module, which just included a method, reloadable?, and was found via ObjectSpace? First of all, it didn’t need a revolution like Dependencies; it was clean, simple, elegant and extendable. It didn’t solve all the problems and had room for improvement, but removing it I think was a mistake. ActiveSupport::Dependencies, unlike it’s predecessor, is a beast. Here’s a quick LOC comparison between activesupport 1.3.1 and 1.4.1 (ignoring hooks in the other gems):

$ grep -v '^\s*#' activesupport-1.3.1/lib/active_support/reloadable.rb | wc -l
28
$ grep -v '^\s*#' activesupport-1.3.1/lib/active_support/dependencies.rb | wc -l
172

$ grep -v '^\s*#' activesupport-1.4.1/lib/active_support/reloadable.rb | wc -l
56
$ grep -v '^\s*#' activesupport-1.4.1/lib/active_support/dependencies.rb | wc -l
538

Not the most reliable comparison, but it gives a good idea of the bloat introduced by, first, dependency tracking and, second, simple deprecating. Most of those lines of code in Reloadable are about how it is not just deprecated, but downright crippled and how evil it was. This is not a good practice for a developing an open source framework which is not easy to upgrade (and we live with it, because Rails is useful). The difficulty of upgrading is precisely because of these badly planned deprecations and backward incompatibilities.

Enough ranting

In this sense, Rails 1.2 is by the far worst gem update I’ve experienced so far. How many blogs have posted about the many little things that needed to be changed to upgrade? How many forward-thinking developers looked in cringing, abject horror when their tests failed left and right after that (apparently) over-optimistic gem update rails? How many people flew to IRC and the mailing lists for help? Let me be so conceited as to introduce a ‘best practice’: discuss major changes with your users.

I realise that Rails is first and foremost a 37signals project, evolved from DHH’s work there and is primarily altered and improved for that company’s purposes, but as an open source project trying to get more users, it’s important to either branch those changes which are special to that company or to be more considerate to the user base. I think Rails development would benefit from:

  • a separate experimental branch (different from Edge trunk) for API changes—Radiant does this quite nicely
  • a highly visible discussion board on rubyonrails.org to discuss major changes with a link to it in IRC and regular updates to the Rails mailing lists

Granted, much of my qualms with Rails 1.2 is with Dependencies and with some good old fashioned hacking, they can fix it. However, I think it and the library from which it has spawned are worth my comment. Dependencies is the most invasive addition to the framework I’ve seen to-date. I appreciate the attempt at innovation, but Rails needs to stop doing this kind of widespread deprecation and fundamentalist intolerance to the unconventional.

I wish the development team would take more pleasure in the diversity of uses of Rails and less perverse pleasure in the militant drilling of young, transitional and experienced programmers. I like the framework, but I don’t work (or want to work) at 37signals.

1 I’ve been aware of my own transgressions for a while. I’m in complete agreement with Mauricio on the topic of warnings. They are there for a reason: to create semantically correct Ruby. We should all be able to run our fantastically short, readable, meta-programmed, overloaded and mixin-loving code with $VERBOSE = true. It isn’t difficult to achieve and it helps in the long-run to make that lovely little code more readable, usable and quiet.

8.5 Minutes

Posted over 7 years back at zerosum dirt(nap) - Home

What were you doing for those eight and a half minutes?
Was it mean, was it petty, or did you realize you were sorry
And that you love them?

It’d be nice to think we could get it right down here just once.
G*d bless the Plan.

Inheritance vs Relational Databases in RoR

Posted over 7 years back at zerosum dirt(nap) - Home

There are three patterns in common use that deal with mapping object inheritance to relational databases. We didn’t discuss any of them in my graduate databases course (sigh), and our friend ActiveRecord implements only one of them: Single Table Inheritance (STI).

STI is what you use when you want to represent an object hierarchy by mapping the union of all attributes found in that class hierarchy to a single underlying database table. Yup, it’s a mouthful. And it works great if the attributes available on the subclasses all tend to be very similar. It’s very fast (comparatively), but makes poor use of space since unused fields are just left null. ActiveRecord’s implementation uses a type field in the database to identify the subclass that the row belongs to.

So what about alternative approaches? There are a couple…

First up is Concrete Table Inheritance, in which there are n database tables for n concrete classes in the hierarchy. Although I’m sure there are cases where this must be useful, I can’t think of a single one. The problem is the silly amount of replication that happens here — fields common to classes will get replicated across tables. This means a lot of replication, more and more as the object hierarchy gets deeper, and a lot of difficulty when it gets to be refactoring time. Bleh, no thanks.

Class Table Inheritance, by contrast, has a table for each class, including interior nodes in the hierarchy (classes that have derivations). A subclass is represented at the database layer as a table that has some number of additive fields plus a foreign key to it’s parent class, which contains the fields that are common to all ancestors.

Conceptually, this seems like the cleanest strategy to me, and the most “OO” of the approaches. However, when we think about the implementation, we realize that performance is going to be a big issue: the class-table strategy is going to require n joins for an object nested n levels into a class hierarchy. Ouch.

Despite that admittedly ugly problem, there has been some interest in implementing Class Table Inheritance in ActiveRecord. In fact there’s some contributed code on the Rails wiki that should allow you to use it as an alternative to STI in Rails. Someone should plugin-ize it, perhaps.

I initially set out to look at alternative DB/object mappings because I was frustrated by the fact that STI wasn’t “accommodating” enough for me. At one point I thought that Class Table Inheritance might be a better match for a specific problem we were looking to solve. It turns out that wasn’t really the case at all; The problem wasn’t STI, it was the way our object hierarchy was structured.

My Lesson Of The Day for February 16th, 2007: If your object hierarchy is relatively shallow, and the single table strategy produces ugly results for you (lots of null fields on subclasses), take another look at the relationships between your objects. STI is a great fit for things that are very closely related to the parent class and require few extra attributes to express that. It’s a lousy fit, otoh, for object relations that aren’t tight like rockstar pants. If both CarPart and DogLeash share the parent class Purchase, they’re probably not good candidates for STI (wink, wink).

Embarrassed as I am to admit it, that was sort of the problem. More on that later, maybe.

Scraping Gmail with Mechanize and Hpricot

Posted over 7 years back at schadenfreude

This quick tutorial will show you how to use mechanize and hpricot to login to gmail and return a list of Unread emails.

Scraping Gmail with Mechanize and Hpricot

Posted over 7 years back at schadenfreude

This quick tutorial will show you how to use mechanize and hpricot to login to gmail and return a list of Unread emails.

Scraping Gmail with Mechanize and Hpricot

Posted over 7 years back at schadenfreude

This quick tutorial will show you how to use mechanize and hpricot to login to gmail and return a list of Unread emails.

Silence is golden

Posted over 7 years back at The Hobo Blog

Just in case you’re wondering, there’s some new screencasts and a pretty cool Hobo update on its way very soon now. Just thought we’d let you know. :-)

Silence is golden

Posted over 7 years back at The Hobo Blog

Just in case you’re wondering, there’s some new screencasts and a pretty cool Hobo update on its way very soon now. Just thought we’d let you know. :-)

Going Solo: Resources Without The 'S'

Posted over 7 years back at zerosum dirt(nap) - Home

In case you missed it in the release announcement for Rails 1.2.2 last week, singular resources are now available in ActionController. You can use them to model singleton resources in your application.

So we’re using technoweenie’s restful_authentication plugin to handle all things authentication-related in our current project, and decided that a singular resource was a good fit for a Session (login, logout) and Account Controller.

Here’s a dumbass-simple example:

map.resource :account, :member => { :activate => :any }

Note the missing ‘s’. No collections for us here, no sir. But what do we get?

GET /account => AccountController#show
GET /account/new => AccountController#new
POST /account => AccountController#create
GET /account;edit => AccountController#edit
PUT /account => AccountController#update
DELETE /account => AccountController#delete
(ANY) /account;activate => AccountController#activate

Hey, that’s just what I needed. And of course we get the standard named routes for the resource like account_url, account_path, etc. Yay for Resources. Yay for REST.

Syspec: System Specifications & Tests using a Rake-like batch file

Posted over 7 years back at Wood for the Trees

Syspec lets you write a batch file a lot like a Rake file, but in common HTTP-style language, to test the existence and proper functioning of all the domains, subdomains, their pages and responses. This project is still very much in its infancy, but I decided to get it out there to get some feedback while I use it to track production servers. This is a sample Syspecfile:

check :"www.rubyonrails.org" do
  accept 200
  get "/"
  accept 404
  get "/syspec"
  get "/lacrima"
end

The above will check that http://www.rubyonrails.org/ responds with 200 and http://www.rubyonrails.org/syspec and http://www.rubyonrails.org/lacrima respond with 404. You can have multiple checks with the same domain and it does not have to be a symbol, though I prefer it that way.

Currently, Syspec features the following:
  • accept(*codes) – response code should be among these codes
  • get, post, delete, put (uses fetch(:request_method, url))
Upcoming features:
  • login “username”, “password”
  • body matching
  • forms
You can grab a copy of it directly from my SVN repository here:
  svn co http://mabs29.googlecode.com/svn/trunk/gems/syspec .
Or download the gem from within the SVN repository (later will be moved to rubyforge):
  svn export http://mabs29.googlecode.com/svn/trunk/gems/syspec/versions/syspec-0.2.1.gem .

Enjoy!

Syspec: System Specifications & Tests using a Rake-like batch file

Posted over 7 years back at Wood for the Trees

Syspec lets you write a batch file a lot like a Rake file, but in common HTTP-style language, to test the existence and proper functioning of all the domains, subdomains, their pages and responses. This project is still very much in its infancy, but I decided to get it out there to get some feedback while I use it to track production servers. This is a sample Syspecfile:

check :"www.rubyonrails.org" do
  accept 200
  get "/"
  accept 404
  get "/syspec"
  get "/lacrima"
end

The above will check that http://www.rubyonrails.org/ responds with 200 and http://www.rubyonrails.org/syspec and http://www.rubyonrails.org/lacrima respond with 404. You can have multiple checks with the same domain and it does not have to be a symbol, though I prefer it that way.

Currently, Syspec features the following:
  • accept(*codes) – response code should be among these codes
  • get, post, delete, put (uses fetch(:request_method, url))
Upcoming features:
  • login “username”, “password”
  • body matching
  • forms
You can grab a copy of it directly from my SVN repository here:
  svn co http://mabs29.googlecode.com/svn/trunk/gems/syspec .
Or download the gem from within the SVN repository (later will be moved to rubyforge):
  svn export http://mabs29.googlecode.com/svn/trunk/gems/syspec/versions/syspec-0.2.1.gem .

Enjoy!

ActiveRecord Association Extensions

Posted over 7 years back at zerosum dirt(nap) - Home

So this is probably old hat to a lot of you, but for those that don’t know, ActiveRecord Association Extensions are a seriously useful widget to have in your Batman Fantasy Camp utility belt.

You’ve no doubt noticed that when you define an association between models, you get a bunch of nifty methods on the resultant collection like push, delete, count, uniq, and so on. But what if you want some bit of functionality defined on an association that isn’t already baked in?

Why we can extend our associations to define our own methods, of course. Let’s see how…

Okay, so we have a relationship defined between two models: articles and opinions. An article has_many opinions. An opinion has a score on it, which is an integer between 1 and 10 representing how a user felt about the article.

Using Association Extensions we can define our own method on the association itself, called total_score:

class Article < ActiveRecord::Base
  has_many :opinions do
    def total_score
      sum(:score)
    end
  end
end

Now you can access this information by writing something like:

article.opinions.total_score

Damn, that’s intuitive. Maybe we want to add another method on the association to retrieve the average opinion of users who voted on this article today:

has_many :opinions do
  def average_opinion_today
    find(:all, :conditions => ["created_at >= current_date()"]).average
  end
end

article.opinions.average_opinion_today

And it gets better. If we find ourselves using the same extensions in more than one place, in true DRY spirit we can build a module and reuse it. When we use the :extend option in an association, we get all the methods in that module mixed in.

module MiscellaneousExtensions
  def average_opinion_today
    find(:all, :condition => ["created_at >= current_date()"]).average
  end

  def total_score
    sum(:score)
  end
end

class Article < ActiveRecord::Base
  has_many :opinions, :extend => MiscellaneousExtensions
end

I put this module in a subdirectory of lib called extensions. You’ll have to add that path to the config.load_paths in environment.rb in order for it to be recognized.

Using these sorts of extensions on my own project earlier today really helped me clean up some troublesome model code. I was just so amped up about it that I had to share.

For more information, see the ActiveRecord::Associations API Reference. Oh and a big shout out to Brian Hogan for pointing me in the right direction.

Too much politics for programmers

Posted over 7 years back at Ryan Tomayko's Writings

One thing I hate about not being able to keep as close an eye on the Python community is the lack of Ian Bicking:

Actually “merging” the two is probably unlikely. It’s the kind of weird political maneuver that open source projects don’t really do, unless they are part of weird “foundations” and have “managers” and stuff like that — it’s too much politics for programmers, and reeks of strategy and plans. You aren’t supposed to announce plans for your software, you are supposed to implement your plans and then announce them. If a merge happens, it should be more like a realization than a strategy.

If you have some spare time or are sick of reading blogs or whatever, I suggest lurking on Python related mailing lists, even if you don’t care about Python. The above is from Ian’s blog but little nuggets of insight like this are very common on the mailing lists. There’s so many smart folks over there…

ANSI Strings (2nd Edition)

Posted over 7 years back at Wood for the Trees

I had a couple of instances where I was adding to the ANSI_CODES constant and creating all sorts of ugly errors. Then a script of mine whispered in my ear its desire for meta-aliases. That prompted a small rewrite of the library.

Ansi codes are now dynamically defined and more easily manipulated without overcomplicating things. Also, I added an RSpec test to the repository for my own piece of mind and to help with refactoring a few things. I don’t know, should I give it a version number? Nah. Just ‘Second Edition’. I don’t envisage any other serious revisions to it any more. What I changed:

  • dumped the array structure for ansi codes
  • added a few non-colour ANSI codes
  • dynamic ansi code definitions: String.define_ansi :alias => “123m”
  • meta-aliases: String.define_ansi :clear_both => [:clear_line, :clear_screen]
  • String#method definitions only if it doesn’t exist already
  • String#method definitions on the fly
  • spec test

And here’s the code:

$ANSI ||= false

class String

  class << self

    attr_accessor :ansi_codes

    def ansi_codes
      @ansi_codes ||= {}
    end

    def define_ansi(definitions = {})
      self.ansi_codes = ansi_codes.merge(definitions)
      define_shortcuts(definitions)
    end

    def define_shortcuts(definitions = {})
      definitions.keys.each do |meth|
        unless instance_methods.include?(meth.to_s)
          define_method(meth) { with_ansi(meth) }
        end
      end
    end

  end

  define_ansi :normal         => "0m",      :bold           => "1m"
  define_ansi :underline      => "4m",      :blink          => "5m"
  define_ansi :reverse_video  => "7m",      :invisible      => "8m"
  define_ansi :black          => "30m",     :red            => "31m"
  define_ansi :green          => "32m",     :yellow         => "33m"
  define_ansi :blue           => "34m",     :magenta        => "35m"
  define_ansi :cyan           => "36m",     :white          => "37m"
  define_ansi :black_bg       => "40m",     :red_bg         => "41m"
  define_ansi :green_bg       => "42m",     :yellow_bg      => "43m"
  define_ansi :blue_bg        => "44m",     :magenta_bg     => "45m"
  define_ansi :cyan_bg        => "46m",     :white_bg       => "47m"
  define_ansi :clear_line     => "K",       :clear_screen   => "2J"
  define_ansi :go_home        => "0H",      :go_to_end      => "80L"

  # Wrap a string with an arbitrary ansi code and the ansi normal code
  def with_ansi(*codes)
    use_ansi? ? "#{sym_to_ansi(*codes)}#{self}#{sym_to_ansi(:normal)}" : self
  end

  # Just a little metaprogramming shortcut
  def ansi_codes; self.class.ansi_codes; end

  private
  def sym_to_ansi(*symbols)
    symbols.inject("") do |string, symbol|
      if code = ansi_codes[symbol]
        string << (code.is_a?(Array) ? sym_to_ansi(*code) : "\e[#{code}")
      end
      string
    end
  end

  # determine whether we have ansi support or ANSI enabled
  def use_ansi?
    $ANSI && RUBY_PLATFORM !~ /win32/i
  end

end

You can grab the new version from my ‘other’ directory at Google Code:

  svn co http://mabs29.googlecode.com/svn/trunk/other/ansi_strings .