Saturday, February 25, 2006

"Facets of Ruby" Brown Bag

I gave a Brown Bag presentation yesterday to the software developers of the Johns Hopkins University Applied Physics Lab. It was good to pour over the lesser known features of Ruby, and though I know more now, I couldn't answer a lot of the basic questions that were asked. A lot of the discussion was on dynamic typing.

Here are some of the answers to questions I couldn't answer right there and then.

Which module is "times" iterator in?

I had the example:

3.times do
print "ho! "
end
# it prints out "ho! ho! ho!"


Since everything in Ruby is an object, the number "3" is also an object, and therefore has methods. "times" is a method in the module Integer. After looking at the source code for "times" (written in C), I stand corrected, it does NOT use "each". Only classes that mixin the module Enumerable will use 'each'. It takes 3 as an argument (hidden to rubyists), and then yields to the block(printing 'ho!' in this case) in a for loop from 1 to 3.

Why would you ever want to add methods at runtime?

I completely forgot that Rails does this as part of it's "magic." Let's say, for example, you have a table called "songs" that has attributes, "id", "title", "artist", and "duration". Normally, you can use a find() method to find all songs by Eric Clapton in the "songs" table:

Song.find(:all, :conditions => ["artist = ?", "Eric Clapton"])

However, rails will also dynamically add methods, based on the attributes of the table:

Song.find_by_title("Bell Bottom Blues")
Song.find_by_artist("Eric Clapton")
Song.find_by_duration(253).


When you query the database for a record, Rails will create a data object with those attributes available as methods on the fly. This way, there is no configuration or mapping between the databse and your data model objects.

How does Ruby go about deciding which method to execute for ambiguous method names?

From the pickaxe book:
"Ruby looks first in the immediate class of an object, then in the mixins included into that class, and then in superclasses and their mixins. If a class has multiple modules mixed in, the last one included is searched first."

In trying it out with test code, you can only use the scope resolution operator to distinguish between two methods with the same name in two different modules if the method in the Modules were declared static.

What is the type of an object when you mix modules into it?

Let's say I have two modules, "Bar" and "Yak", and a class "Foo" that mixes "Bar", but not "Yak". Using the kind_of? method:

f = Foo.new
f.kind_of? Foo # => true
f.kind_of? Bar # => true
f.kind_of? Yak # => false


So yes, an object instanciated will also be a type of modules that you mixed in.

If I really wanted type checking, what could I do?

You can use the reflection methods, such as kind_of?() or instance_of?(). But since what you're really interested in is if the object will fail on a call that you make, it might be better to to call responds_to?(), to see if the object responds to a particular method.

The pickaxe book warns that you should do type checking if you really have a good reason for it. Otherwise, it's more code to maintain, and has less flexibility in future enhancements.

No comments:

Post a Comment