222 Enumerable ObjectSpace

Description

Kaixo Rubyists,

This week’s quiz was suggested by Trans.

ObjectSpace has the method #each_object. I’ve always wanted to alias that to #each and make ObjectSpace Enumerable. But there’s a catch, #each_object takes an argument and Enumerable does not pass arguments on to the #each method. So how does one resolve this issue? What’s the easiest way to make ObjectSpace Enumerable?

One could always take the brute force approach and completely re-implement Enumerable module to pass along the arguments. But I’m hoping there are much more elegant solutions to be had.

Have Fun!

Summary

(Sorry for the delays!)

There were many great solutions to this week’s quiz.

James Coglan provides us with a compact solution using method_missing.

class << ObjectSpace
  def method_missing(sym, *args, &block)
    mod  = args.first.is_a?(Module) ? args.shift : nil
    enum = enum_for(*[:each_object, mod].compact)
    enum.__send__(sym, *args, &block)
  end
end

When calling an Enumerable method like map or each on ObjectSpace it will trigger method_missing as James’s solution does not have ObjectSpace include the Enumerable methods directly. The first argument, if present and a Module will be extracted. An enumerator is then created using enum_for and passing the given Module, if present, to the each_object method of ObjectSpace. The original method invoked that brought us into method missing in then invoked on the new enumerator with the remaining arguments and block. This will work for map as well as inject as long as the first argument is a Module. Methods such as map and each may be called with no Module given, in which case the block is called once for each living, nonimmediate object in the Ruby process. The density of meta-programming in James’s solution is a paragon of virtue and a good test of one’s meta-programming knowledge.

Benoit Daloze’s solution uses meta-programming to define new methods that accept a Module parameter for all the 0 arity methods in Enumerable. each is also redefined to accept a Module parameter, though this is not used by the redefined methods, they use to_enum and select to filter out all instances of the given Module.

objects = to_enum.select { |o| o.is_a? mod }

Robert Klemme added an arg method to ObjectSpace which stored the parameter in a thread local stack variable, so that calls may be nested. Robert’s arg method takes a block which is evaluated within the context of ObjectSpace. The each method is defined to call each_object with the parameter given by the top of the thread local stack variable.

Stefano Crocco and Intransition also gave some good solutions, view the attachment for full details.

Thank you everyone for you solutions to this week’s quiz. It was fun and really mind stretching. Keep the suggestions coming!

Enumerable ObjectSpace (#222) - Solutions


Saturday, October 24, 2009