### LazyEnumerable ### Written By Blaine Buxton Copyright 2006 ### ### It takes a functional approach to the common Enumerable protocol of select, collect, and reject. ### By functional, I mean the collection is not changed nor is a new modified one created. ### The blocks are kept around until they are absolutely needed. ### I have been wanting this functionality for some time because it's nice for large collections. ### If you have a collection in which you are calling a lot selects, rejects, or collects on, ### then this will not create the intermediate collections. ### It will wait until you ask something of the collection where it can not delay the answer. ### This should make these chained operations must faster on large collections. ### ### This was a lot of fun to program and it's not that big. Take whatever you want from it! ### ### Read the tests at the bottom to see how it works! ### People have told them this hurts their brain. If you learn anything from this ### please feel to send me an email! ### ### There's example of Lisp-like macros (not quite, but bear with me), meta-programming, ### and lots of closures of course. class LazyEnumerable ## ## Remove any unnecessary methods so that method_missing is invoked ## def self.wack_all_my_methods to_wack=instance_methods.reject do |each| ['===','method_missing'].include?(each) || each =~ /^__/ end to_wack.each do |each| alias_method(("_" << each), each) undef_method(each) end end ## ## Wack all of my methods, then apply Enumerable and define my constants ## and class variable for cached method calls ## wack_all_my_methods include Enumerable PLACEBO=lambda {|each| each} @@cached={} ## ## Meta-programming to provide a shorthand for extensions to let themselves ## known so that they can be found on the method_missing. ## def self.define_for(method_name) undef_method(method_name) @@cached[method_name]=self end ## ## Lookup a subclass for the particular method. Basically, this is so that each ## template will provide its own functionality for each ## def self.class_for(method_name) @@cached[method_name] end def self.iterator_creator(*method_names) method_names.each {|each| iterator_creator_for(each)} end ### ### This code creates methods for an iterator method (:collect, :select, etc) ### if invoked with no arguments (no blocks either). It returns a LazyIterator ### on no block because it them assumes you are creating higher order methods. ### This allows you to do things like big_collection.reject.nil? instead of ### big_collection.reject {|each| each.nil?}. I hate the use of eval here even though ### it is Lisp-ish (macros). I think I should have been able to do with define_method ### but you can not use the same semantics passing blocks as arguments to blocks as ### you do in methods. Variable arguments failed as well. But, eval keeps the same job ### done even if in an inelegant way. Also, it's uglier than hell with the <