Less Used Ruby APIs

If you’ve ever developed something using Ruby then you know it’s a blast to use. It flows well, reads well, and has lots of cool ways to get things done. Beyond the day-to-day APIs you might be used to, there lies a bunch of goodies that are often overlooked or never seen until now! In this article I’ll explore some of those gems, no pun intended, that could deliver you straight to Ruby zen.

Kernel

The Kernel module provides a variety of methods which are available to all objects that inherit from Object, since Object mixes in Kernel. Why do I make this distinction? Well, as of Ruby 1.9 the BasicObject class has been added and is now the parent of all classes. The BasicObject does not have the methods provided by the Kernel module.

__callee__

This oddly named function returns the name of the current method as a Symbol.

def add(a, b)
    log("#{a} + #{b}", __callee__)
    a + b
end

def log(msg, caller)
    puts "##{caller}: #{msg}"
end

In the example above the log method takes two parameters: a message and the caller. The add method is making use of the __callee__ function to pass the name of its method to the log function. If you fire up the irb console, type in those two methods followed by add 3,5 you’d end up with:

#add: 3 + 5
=> 8

Ok, so that’s pretty basic but kinda nifty, right? Sure you could just pass :add to the log method instead of using __callee__, after all you know you’re in the add method right? The slight advantage in the use case provided above is that if you were to rename the add method to my_super_add_method you wouldn’t have to update your call to the log method. Feel free to come up with some other handy uses and post them in the comments section of this article.

at_exit

If you ever needed to run some code just before your program exits then you should check out at_exit. It registers a block as a handler which gets invoked when a Ruby process is about to exit. In fact you can register multiple handlers as shown in the following example, at_exit_example.rb.

at_exit {
    puts "the program is exiting"
}

at_exit {
    puts "the program is really exiting"
}

puts "doing some stuff"
raise "Uh oh, something bad happened!"

The handlers will be invoked in last in, first out (LIFO) order. If you put the above code into a file, perhaps named at_exit_example.rb, and ran it ruby at_exit_example.rb, you’d get:

doing some stuff
the program is really exiting
the program is exiting
at_exit_example.rb:10:in `<main>': Uh oh, something bad happened! (RuntimeError)

As you’ve seen, at_exit handlers will be invoked when the Ruby process is about to exit, even if it’s because of an unhandled exception. This could be useful for cleaning up some files that might have been created by your program, closing connections to servers, or logging some context that might help debug an issue with the program (if it’s a program that shouldn’t normally exit on its own).

block_given?

This may be one function you’ve run across already, but if not, now’s a good time to learn about it. If you’ve ever passed a block to a function then there’s a good chance the function being called is using block_given?. Below is an example of how block_given? could be used.

def sum(values)
    error = result = nil
    begin
        result = values.inject do |total, value|
            total += value
        end
    rescue Exception => ex
        error = ex
    end

    if block_given?
        yield result, error
    else
        return result unless error
        raise error
    end
end

sum((1..5)) do |result, error|
    puts "Error: #{error}"       # => nil
    puts "Result: #{result}"        # => 15
end

The sum function takes a collection of values, calculates their sum and returns the result. The function checks to see if a block has been given, hence the block_given? call, and if it has then it will yield the result and error. If an exception occurred while performing the sum then it will be caught and given back to the block.

However, if a block isn’t given the sum function will calculate the result and return it. If an exception occurs in this scenario then the sum function will raise it instead of returning nil or some other value to indicate a problem. If you want to allow for a block to be passed in to your functions, try giving block_given? a whirl!

Object

The Object class is oddly described as:

Object is the root of Ruby’s class hierarchy. Its methods are available to all classes unless explicitly overridden.

I say oddly because, as I mentioned earlier, BasicObject is the new root, top-level class in Ruby’s class hierarchy. I suppose the documentation just needs to be updated, something you or I could volunteer to do ;).

The Object class gets a lot of its behavior by mixing in the Kernel module, but also has some of its own goodies such as method described below.

method

Ever wonder how you might get a reference to a method? Many languages provide this capability but check out how concise it is with Ruby:

m = Calculator.method(:sum)  # => assume "sum" is a class method
m.call 3, 4  # => 7

Try doing that in Java and you’ll easily add more code including multiple catch clauses to handle the myriad of potential exceptions that might be thrown.

ObjectSpace

The ObjectSpace module is described as follows:

contains a number of routines that interact with the garbage collection facility and allow you to traverse all living objects with an iterator.

Cool, huh? I thought so which is why I’ll show you each_object and _id2ref.

each_object

The each_object function provides an iterator over all live objects (for this Ruby process). The enumerator can be filtered by type in case you’re only interested particular types of objects. You might use each_object, and the ObjectSpace module in general, to aid in debugging or profiling.

class Foo; end

ObjectSpace.each_object(Foo) do |foo_obj|
    puts foo_obj
end    # => 0  ...there are no instances yet.

# create a new instance and print the object_id
puts Foo.new.object_id  # => 2160309640

ObjectSpace.each_object(Foo) do |foo_obj|
    puts foo_obj.object_id   # => 2160309640
end

Of course you can iterate over any type of object, not just your own types. Go ahead, take a look at all the String objects!

_id2ref

If, for some reason, you happen to know the object_id of an object running in your Ruby process, then you’re in luck. You can grab a reference to that object using ObjectSpace._id2ref.

f = Foo.new.object_id   # => 2160309640
ObjectSpace._id2ref(2160309640) == f  # => true

Plenty More

Hopefully you’ve picked up something new and cool after reading this, but I bet you want more, right? If so you might want to check out some of these:

The point is, Ruby has lots of interesting code available for your exploration. Do you have experience with some less-common Ruby classes, modules or functions? If so, post a comment and share with everyone!

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://www.marnen.org Marnen Laibow-Koser

    Nice…but your _id2ref example is wrong. I think what you wanted was:

    f = Foo.new
    f_id.object_id # => 2160309640
    ObjectSpace._id2ref(f_id) == f # => true