Ruby Docstrings

I can’t be the first person to come up with this, but since Google doesn’t show anything, I figured I’d share it.

Take the following code:

class Rect
  d"Calculates the area of the rectangle." do
    def area
      length * height
    end
  end

  d"The height of the rectangle." do
    attr_accessor :height
  end

  d"The length of the rectangle." do
    attr_accessor :length
  end
end

This lets us do:

irb(main):035:0> Rect.__doc__(:length)
=> "The length of the rectangle."
irb(main):036:0> Rect.__doc__(:area)
=> "Calculates the area of the rectangle."

Yay! Docstrings! So, how does this work?

Well, first of all, we define our methods Object#d and Object#__doc__:

class Object
  def self.d(doc, &block)
    block.binding = binding
    pre_instance_methods = self.instance_methods
    block.call
    new_instance_methods = self.instance_methods - pre_instance_methods
    new_instance_methods.each do |method_name|
      (@doc_lookup ||= {})[method_name.to_sym] = doc
    end
  end

  def self.__doc__(method_sym)
    @doc_lookup[method_sym]
  end
end

Note that they’re both class methods, so when we want information on an instance method, we don’t actually need an instance lying around. Also note that this is easily extensible to class methods.

If you were paying attention to that chunk of code, you will have noticed something peculiar:

block.binding = binding

This doesn’t work without a little help:

class Proc
  attr_writer :binding
end

The big news here is that this works at all. It amazed me to find that bindings of Proc objects were writable without harmful side effects. The way I’m using it here allows me to pop open the Object#d method, so the method definitions proceed as if it didn’t exist.

Naturally, this isn’t perfect as a docstring solution. I’m not fond of the method names, or the syntax – especially because this technique precludes mutliline docstrings – but it’s a step towards one. There’s another approach possible using Binding#of_caller – maybe I’ll try that next…

Leave a Reply

Entries (RSS)