From d401b1471d26f9468734fc9645b0e26e97682e6d Mon Sep 17 00:00:00 2001 From: Mike Dvorkin Date: Tue, 9 Nov 2010 19:48:43 -0800 Subject: [PATCH] Finally figured it out: properly intercept Array#grep --- lib/ap/core_ext/array.rb | 58 ++++++++++++++++++++++++++++++---------- spec/methods_spec.rb | 22 +++++++++++++++ 2 files changed, 66 insertions(+), 14 deletions(-) diff --git a/lib/ap/core_ext/array.rb b/lib/ap/core_ext/array.rb index 894135e..3944da8 100644 --- a/lib/ap/core_ext/array.rb +++ b/lib/ap/core_ext/array.rb @@ -3,18 +3,18 @@ # Awesome Print is freely distributable under the terms of MIT license. # See LICENSE file or http://www.opensource.org/licenses/mit-license.php #------------------------------------------------------------------------------ +# +# The following makes it possible to invoke awesome_print while performing +# operations on method arrays, ex: +# +# ap [].methods - Object.methods +# ap ''.methods.grep(/!|\?/) +# +# If you could think of a better way please let me know: twitter.com/mid :-) +# class Array - # - # The following makes it possible to invoke awesome_print while performing - # operations on method arrays, ex: - # - # ap [].methods - Object.methods - # ap ''.methods.grep(/!|\?/) - # - # If you could think of a better way please let me know: twitter.com/mid :-) - # [ :-, :& ].each do |operator| - alias_method :"original_#{operator.object_id}", operator + alias :"original_#{operator.object_id}" :"#{operator}" define_method operator do |*args| arr = self.send(:"original_#{operator.object_id}", *args) if self.instance_variable_defined?('@__awesome_methods__') @@ -24,10 +24,40 @@ class Array arr end end - - alias_method :original_grep, :grep - define_method :grep do |*args, &blk| - arr = original_grep(*args, &blk) + # + # Intercepting Array#grep needs a special treatment since grep accepts + # an optional block. + # + alias :original_grep :grep + def grep(pattern, &blk) + # + # The following looks rather insane and I've sent numerous hours trying + # to figure it out. The problem is that if grep gets called with the + # block, for example: + # + # [].methods.grep(/(.+?)_by/) { $1.to_sym } + # + # ...then simple: + # + # original_grep(pattern, &blk) + # + # doesn't set $1 within the grep block which causes nil.to_sym failure. + # The workaround below has been tested with Ruby 1.8.7/Rails 2.3.8 and + # Ruby 1.9.2/Rails 3.0.0. For more info see the following thread dating + # back to 1993 when Ruby 1.8.0 was as fresh off the grill as Ruby 1.9.2 + # is in 2010 :-) + # + # http://www.justskins.com/forums/bug-when-rerouting-string-52852.html + # + # BTW, if you figure out a better way of intercepting Array#grep please + # let me know: twitter.com/mid -- or just say hi so I know you've read + # the comment :-) + # + arr = unless blk + original_grep(pattern) + else + original_grep(pattern) { |match| eval("%Q/#{match}/ =~ #{pattern.inspect}", blk.binding); yield match } + end if self.instance_variable_defined?('@__awesome_methods__') arr.instance_variable_set('@__awesome_methods__', self.instance_variable_get('@__awesome_methods__')) arr.reject! { |item| !(item.is_a?(Symbol) || item.is_a?(String)) } # grep block might return crap. diff --git a/spec/methods_spec.rb b/spec/methods_spec.rb index 1362e4d..743892b 100644 --- a/spec/methods_spec.rb +++ b/spec/methods_spec.rb @@ -384,4 +384,26 @@ describe "Methods arrays" do out = Hello.methods.grep(/^m\d$/).ai(:plain => true) out.should == "[\n [0] m1() Hello\n [1] m2() Hello\n [2] m3() Hello\n]" end + + it "obj1.methods.grep(pattern, &block) should pass the matching string within the block" do + class Hello + def self.m_one; end + def self.m_two; end + end + + out = Hello.methods.grep(/^m_(.+)$/) { $1.to_sym } + out.should == [:one, :two] + end + + it "obj1.methods.grep(pattern, &block) should be awesome printed" do + class Hello + def self.m0; end + def self.none; end + def self.m1; end + def self.one; end + end + + out = Hello.methods.grep(/^m(\d)$/) { %w(none one)[$1.to_i] }.ai(:plain => true) + out.should == "[\n [0] none() Hello\n [1] one() Hello\n]" + end end