8 Aug 2010 fxn   » (Master)

When Classes Leak Into Ruby Contracts

Sometimes Ruby APIs document non-Rubyesque expectations that artificially separate "classes" and "objects", a la Java. I'd like to give you a few examples to depict what I mean, and explain why that's artificial later.

First example, the Rack specification says that

A Rack application is an Ruby object (not a class) that responds to call.

That's not very idiomatic, why classes are banned? A Ruby programmer would expect this shorter contract:

A Rack application is an Ruby object that responds to call.

That's it, the nature of the object is irrelevant to Rack, the only thing that matters is that the object responds to call (with such and such signature). Indeed a class that responds to call is a perfectly valid Rack application. The implementation is Rubyesque, but the wording in the docs is not.

Another example taken from the chapter on routing of O'Reilly's Rails 3 in a Nutshell:

Constraints may either be specified as a hash, a class implementing a matches? class method, or a class that responds to a call method, such as a Proc object.

The suspicious bit in that contract is "a class implementing a matches? class method". Indeed there's no requirement in the routing system that you pass a class. All it matters is that you pass any object that responds to matches?, see:

    constraint.respond_to?(:matches?) && !constraint.matches?(req)

That's idiomatic Ruby, where the interface is the only thing that matters, classes are irrelevant.

Classes Are Ordinary Objects

Technically in Ruby there are no "class methods" as opposed to "instance methods". Ruby only has instance methods. Let me summarize how this works.

If you define a Person class having a name instance method, instances of Person respond to name. No surprises here. But individual person instances can respond to more stuff:

    def person.custom_method

In the example above, the object stored in the person variable also responds to custom_method. Such a method targeted to a particular instance is called a singleton method. You can for example build simple mocks this way:

    o = Object.new
    def o.name
    # Now pass o to code that expects anything responding to #name.

And you can also override methods defined in the class of the object.

In Ruby classes are objects. When you write Person, that's an ordinary constant. Totally ordinary. It is the same kind of ordinary constant as

    X = 1

No difference. You can think of

    class Person

as being equivalent to

    Person = Class.new

The Ruby interpreter then processes the class definition body, but as far as the constant is concerned that's it. In fact, if you have an anonymous class and assign it to a constant later, then it gets its name automatically after the constant's name.

So, this is a key point, the Person constant is ordinary, it happens to evaluate to a class object, the same way X above evaluates to an integer. And here is when Ruby deviates from other OO languages: classes are ordinary objects also, objects of type Class:

    klass = Person
    person = klass.new

That works because Person just evaluates to a class object, and as with any other object you can store classes in variables and pass them around, and that object responds to the new method, the same way person responds to name. Why? Because klass is an object of the class Class, which defines new among its instance methods. Simple and elegant.

So at this point you need to forget a bit mental schemas coming from other languages and open your mind to accept the derivations of this particular OO model.

Since classes are ordinary objects, you can also define singleton methods on them, the same way we did with person before. Do you recognize now this idiom?

    class Person
      def self.find_by_name(name)

In the body of a class self is the class object in scope, and so that is just defining a singleton method on it.

All classes are instances of Class, a "class method" is any method a class responds to, which may come from Class, or be defined for particular classes, that is, singleton methods.

Class Methods, Fine

I am fine with the term "class method" in Ruby as long as we know what we are talking about. Technically Ruby has no such things, everything are instance methods, but you can take "class method" as short for "an instance method of the class object". Depending on their intended usage, class methods are also referred to as "macros", also a convenient term, think has_many in Active Record.

But even if you can talk about "class methods" in that sense, you rarely need to tell classes from non-classes in API contracts based on interfaces.

Latest blog entries     Older blog entries

New Advogato Features

New HTML Parser: The long-awaited libxml2 based HTML parser code is live. It needs further work but already handles most markup better than the original parser.

Keep up with the latest Advogato features by reading the Advogato status blog.

If you're a C programmer with some spare time, take a look at the mod_virgule project page and help us with one of the tasks on the ToDo list!