Dave has posted a clever piece of code,
showing how you could do runtime class checking in Ruby (or a Ruby-like
language) by simply wrapping methods and recording the first class that is
used for each method parameter. In his example, he uses it to throw an
error if any subsequent invocation has a parameter of a class different
from the first usage of that parameter. So, the following would fail:
bob.age = 32 bob.age = Duck.new
Though he warns that this is just a "coffee-break implementation", the actual code is remarkably simple as well. I consider this to be a hallmark of Dave’s.
As anyone in the dynamic programming languages world knows, "type checking"(1) is a touchy and usually unproductive subject (RubyTypesAndClasses). The common complaint is that, without "type" information for method parameters, code is hard to understand and maintain. I don’t generally find this to be true for myself, but I can see and understand that sometimes it would be helpful to have some hints as to what kinds of objects a library author passes into certain method calls.
The simplest solution to this—which wouldn’t require a compromise of the design philosophy of the language—would be for all library authors to simply document their code, giving their users a hint as to what each method expects. We’ve all heard that one before. Even those of us with the best intentions do, on average, a poor job documenting our own code. Additionally, what’s obvious to me may not be obvious to you. So as a library author, I will probably either overdocument (waste my own time and clutter the code) or underdocument.
Taking a cue from Dave, I did my own coffee-break implementation(2) of a hack that would auto-document the class of parameters for a given set of methods. It’s implemented as a modification to rdoc. The idea is that you can "instrument" your code temporarily, run through a typical usage of it, and have Ruby keep track of—and save—class information for the method parameters of specified classes.
I’ve written a servlet (which I’ll talk about in more detail in a later post) that I would like to document via rdoc. Assuming the servlet is called "ContinuationServlet", I can auto-generate parameter class hints for this servlet by adding the following to my code:
require 'class_hints' wrap_methods(ContinuationServlet) ... (invoke webrick normally here) ... File.open("seasidehints.out", "w") do |file| file.write(Marshal::dump(Kernel::METHOD_PARAM_HINTS)) end
This will install a wrapper around every method of ContinuationServlet, which will save class information for each parameter in the nested hash, Kernel::METHOD\_PARAM\_HINTS. After having clicked around through the application with a web browser, we just dump this to disk as a marshalled Ruby object.
Next, we feed this hash to rdoc when we document:
rdoc --param-hint-file=seasidehints.out pm_seaside.rb
You can see the output here. For the lazy, documented methods now look something like this:
Full code listing:
Like Dave, I’m just throwing this up here for comment. I’m not sure anyone will find it useful, and the implementation is definitely a hack. But, it seems like something like this might help to quiet some of the complaints that half the population seems to have about dynamically typed languages. And, for example, could FreeRIDE use a trick like this to provide intellisense-like hints in the IDE?
(1) The word "type" is frequently misused in the Ruby world to mean "class". Matz actually deprecated the \#type message on Object to clarify that a Ruby object’s "type" is more focused on its capabilities than its class. Class is little more than a convenience mechanism.
(2) It was a pretty long coffee break :)