Overriding the Backtick in Ruby

I’ve been planning this post for a long time, and Magnus Holme’s recent article on overriding unary operators gives me the perfect lead-in.

Ruby has great syntax sugar for quoting strings. You’ve got your single- and double-quotes; you’ve got slashes for Regexps and you’ve got a whole menagerie of %-quotes, such as %r for regular expressions: %r(/.*/).

But Ruby is a bit uptight about its quotes; if there’s a special kind of quoting you want to do that Matz didn’t think of, there’s no way to add it to the language. For instance, what if you wanted a way to quote URIs and have them be immediately parsed into URI objects, something like this:

uri = %u(http://google.com) # Doesn't work!

We can’t do anything like that… can we?

Well, maybe we can. You know how backtick quoting works, right? Put a string in backticks and you get the result of that string evaluated as a shell command:

`uname -srm` # => "Linux 2.6.36-1-lowlatency x86_64n"

What you might not know is that the backtick, unlike other quotes, is technically an operator defined in Kernel. And what is defined in Kernel can be overridden:

require 'uri'
module BacktickURI
  def `(uri)
    URI.parse(uri)
  end
end

include BacktickURI

`http://google.com`             # => #<uri::http:0x7f4d30c3e7c0 URL:http://google.com>

And there you have it – your own custom backtick quotes.

Is this a good idea? Probably not. I certainly don’t recommend overriding the backticks at the root level as I’m doing here – this is a technique best limited to certain DSL contexts. But for better or for worse, now you know that even (some) quotes are overridable in Ruby.

5 comments

    1. Most (but not all) operators can be overridden in Ruby. The backtick is a special case – quotation marks aren't usually seen as an “operator” in most languages, but Ruby treats the backtick (and only the backtick) as an operator.

  1. And something nifty you didn't mention: the backtick method is called on the implicit receiver (that is, “self”). That means that you can do this:

    require 'net/http'

    class Google
    include BacktickURI

    def search_page_html
    url = <a href="http://www.google.com" rel="nofollow">http://www.google.com</a>/
    res = Net::HTTP.start(url.host, url.port) {|http|
    http.get('/')
    }
    res.body
    end
    end

    Still not a great practice in general, but you can limit the damage to a single class instead of changing the meaning of backticks everywhere. As you say, it could find a nice use in some sort of DSL.

Leave a Reply

Your email address will not be published. Required fields are marked *