Hi --

On Sat, 16 Jun 2007, Giles Bowkett wrote:

>> > Why should this inspire fear?
>> 
>> Code injection attack to own a RoR site possibly?  That would be my guess.
>
> Sort of. I'm trying to use it to build a Rails plugin called
> acts_as_fox, which overrides every method on a model to return "chunky
> bacon!" (It's not really that terrifying, I just kind of had a Dr.
> Frankenstein moment, drunk on power type thing.)
>
> Unfortunately, applying Rick's code directly to an ActiveRecord model
> doesn't quite accomplish this, because it's missing the superclass
> methods, but applying it to ActiveRecord::Base doesn't work either. I
> did get it to work by doing it twice, both on the actual model and on
> ActiveRecord::Base itself, but that's very unsatisfying, because I
> solved the problem by cutting and pasting. (I think I understand why
> it worked; ActiveRecord::Base attaches a lot of methods to its
> subclasses, instead of having them inherited directly.) It also fails
> to really accomplish what I want to do, because it means that making
> one model acts_as_fox destroys all the other models. (I also need to
> attach a method_missing to return "chunky bacon!" but that part's
> obviously trivial.)
>
> Really the quickest way to accomplish this would be to simply pop the
> model out of its inheritance hierarchy - redefine the model not to
> have any superclass except Object - but I don't know if that's
> possible. Trying it in the most obvious way (class Foo < Object; end,
> where Foo was already defined Foo < ActiveRecord::Base) results in a
> TypeError with the message "superclass mismatch."

I do sometimes wonder what would happen if the ancestry array were
writeable.  It could be interesting.  I haven't thought through the
possible pitfalls.

> But there must be a clean way to open up the class, grab all its
> methods, including those derived from superclasses, and simply
> reassign them. Something like
>
> Foo.instance_methods(true).each{|m| Foo.instance_eval("alias
> :chunky_bacon " + m)}

Here's a little demo that does pretty much that (sparing the _ methods
like __send__):

class Object
   def mask
     puts "I'm masking a method"
   end
end

class C
   def x
     puts "x"
   end

   def y
     puts "y"
   end
end

class D < C
   def z
     puts "z"
   end
end

class D
   instance_methods.reject {|m| /^_/.match(m) }.each do |m|
     alias_method m, :mask
   end
end

C.new.x   # x
D.new.x   # I'm masking a method
D.new.z   # I'm masking a method


David

-- 
* Books:
   RAILS ROUTING (new! http://safari.awprofessional.com/9780321509246)
   RUBY FOR RAILS (http://www.manning.com/black)
* Ruby/Rails training
     & consulting:  Ruby Power and Light, LLC (http://www.rubypal.com)