From: Intransition Date: 2009-11-02T05:51:26+09:00 Subject: Re: Enumerable ObjectSpace (#222) I was impressed with these solutions. The use of an instance variable to store the argument I found particularly clever. I had worked on something like this a long time ago. At the time I completely reimplemented Enumerable by hand to accept #each arguments (it was the first time I ever used TDD, btw). I later realized afterward a bit of meta-programming could make all of it a whole lot easier, so I created what is now enumargs.rb (gem install enumargs). It is similar to Benoit's solution. require 'enumerator' # This is a simple reimplementation of the core Enumerable module # to allow the methods to take and pass-on arbitrary arguments to the # underlying #each call. This library uses Enumerator and scans # Enumerable so it can alwasy stay in sync. # # NOTE Any Enumerable method with a negative arity cannot pass arguments # due to ambiguity in the argument count. So the methods #inject and #zip # do NOT work this way, but simply work as they do in Enumerable. # However the method #find, and it's alias #detect, have been made to work # by removing its rarely used optional parameter and providing instead an optional # keyword parameter (:ifnone => ...). Please keep these difference in mind. # # require 'enumargs' # # class T # include Enumerable::Arguments # def initialize(arr) # @arr = arr # end # def each(n) # arr.each{ |e| yield(e+n) } # end # end # # t = T.new([1,2,3]) # t.collect(4) # #=> [5,6,7] # module Enumerable module Arguments def self.wrap_enumerable_method( methodname ) m = methodname meth = Enumerable.instance_method(m) arity = meth.arity case arity <=> 0 when 0 class_eval %{ def #{m}( *args, &yld ) enum_for(:each, *args).#{m}( &yld ) end } when 1 class_eval %{ def #{m}( *args, &yld ) args, each_args = args[0...#{arity}], args[#{arity}..-1] enum_for(:each, *each_args).#{m}( *args, &yld ) end } else class_eval %{ def #{m}( *args, &yld ) enum_for(:each).#{m}( *args, &yld ) end } end end Enumerable.instance_methods(false).each do |m| wrap_enumerable_method( m ) end # Make exception for #find (a negative arity method) to accept # keyword argument. # # ObjectSpace.find(Class, :ifnone=>lambda{1}) { |e| ... } # ObjectSpace.find(Class, :ifnone=>lambda{1}) { |e| ... } # def find(*args, &yld) # future use **keys ? if Hash === args.last and args.last.key?(:ifnone) ifnone = args.last.delete(:ifnone) args.pop if args.last.empty? enum_for(:each, *args).find( ifnone, &yld ) else enum_for(:each, *args).find( &yld ) end end alias_method :detect, :find end end With that, the solution to #222 is simply: class << ObjectSpace include Enumerable::Arguments alias each each_object end Eg. ObjectSpace.select(Class){ |c| c < Exception } Thanks.