Doug Livesey
2007-Aug-20 18:04 UTC
Can''t alter (decorate) ActiveRecord::Base#method_missing
Hallo -- I''m trying to decorate the default behaviour of ActiveRecord::Base#method_missing() with the following code: <CODE> class ActiveRecord::Base alias_method( :b023_method_missing, :method_missing ) \ unless method_defined?( :b023_method_missing ) def method_missing( method, *args ) puts "\n***** -- In method_missing()" b023_method_missing( method, args ) end end </CODE> This, to my thinking, should simply do what ActiveRecord normally does with calls to the attributes like "model.name", etc. However, what is happening is that I am getting a "stack level too deep" error. When run in the console, I get "***** -- In method_missing()" printed a million times, then the stack overflow error. It seems to be that the the call to alias_method is happening twice (despite the unless guard), thus creating an infinite loop. Has anyone successfully done something like this with ActiveRecord, and if so, could they please tell me how! Cheers, Doug. -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Bryan Duxbury
2007-Aug-20 19:53 UTC
Re: Can''t alter (decorate) ActiveRecord::Base#method_missing
I''m not sure why that doesn''t work, unless the method_defined? call actually hits the original method_missing. In any case, is there a reason why you need to use alias_method instead of just overwriting the method in your model class? You can then just call super() to hit the original method. It''s what inheritance is there for. -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Doug Livesey
2007-Aug-20 19:57 UTC
Re: Can''t alter (decorate) ActiveRecord::Base#method_missing
Bryan Duxbury wrote:> In any case, is there a reason why you need to use alias_method instead > of just overwriting the method in your model class? You can then just > call super() to hit the original method. It''s what inheritance is there > for.That''s what I''m looking at now. Basically, I want to create a plugin that allows delegation of methods to encapsulated models upon the raising of NoMethodError in its own method_missing() method. Cheers, Doug. -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Bryan Duxbury
2007-Aug-20 20:05 UTC
Re: Can''t alter (decorate) ActiveRecord::Base#method_missing
Then what you probably want to do is define a module that, when included, dynamically overwrites the method_missing method. You''d have to do an "include MethodMissingWhackiness" in each class that makes use of that functionality. I''m not sure I understand your use case, though. You want to implement subsets of your model''s functionality in separate models that will be what, nested classes? And you''d like the parent model to transparently delegate those methods? That seems somewhat confusing to me. Maybe instead, you should consider implementing the subsets of methods as modules and just including them into the parent class. -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Doug Livesey
2007-Aug-20 20:14 UTC
Re: Can''t alter (decorate) ActiveRecord::Base#method_missing
Bryan Duxbury wrote:> Then what you probably want to do is define a module that, when > included, dynamically overwrites the method_missing method. You''d have > to do an "include MethodMissingWhackiness" in each class that makes use > of that functionality. > > I''m not sure I understand your use case, though. You want to implement > subsets of your model''s functionality in separate models that will be > what, nested classes? And you''d like the parent model to transparently > delegate those methods? That seems somewhat confusing to me. > > Maybe instead, you should consider implementing the subsets of methods > as modules and just including them into the parent class.You could be right. I think, however, that this could be useful as a simple plugin for those situations where you have, say, a Person class that can take on the role of Salesman, Customer, Manager, etc. There is, in fact, a handy plugin that looks like it might answer this at: http://daveverwer.com/2007/3/16/activerecord-delegation (I''ve just been referred to it by its author on a UK Ruby list.)> You''d have > to do an "include MethodMissingWhackiness" in each class that makes use > of that functionality.Yes -- although to make it look more "Railsy", I''ve reopened ActiveRecord::Base and put it in the following method: def self.delegates_to( delegatee, options ) # other setup stuff include MethodMissingWhackiness end & cheers for your input, man! Doug. -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Doug Livesey
2007-Aug-21 14:21 UTC
Re: Can''t alter (decorate) ActiveRecord::Base#method_missing
Hi again -- just for completeness -- I''ve got what I wanted to happen working. It''s an ongoing thing (so delegated validations are next), but the working code so far (after considerable duck-punching) is: <CODE> require "active_record" module Biot023 module ActsAsDelegator def method_missing( method, *args ) begin super rescue NoMethodError => err self.methods.grep( /^_fetch_delegatee_/ ).each do |mname| begin model = self.method( mname ).call raise( "#{ mname } failed to return model in method_missing" ) \ unless model _method, _args = [ model.method( method.to_sym ), *args ] \ rescue _method, _args = [ model.method( :method_missing ), [ method, *args ] ] return _args ? _method.call( *_args ) : _method.call rescue => inner_err # do nothing end end raise end end end end class ActiveRecord::Base # Delegate to a delegatee, whose id must be referenced from this model, just # the same as a belongs_to() call. In fact, this takes the same options as # belongs_to(), as it then calls it. def self.delegates_to( delegatee, options={} ) classname = options[:class_name] || delegatee.to_s.camelize belongs_to( delegatee.to_sym, options ) include Biot023::ActsAsDelegator class_eval( <<-EOS, "_fetch_delegatee", 1 ) def _fetch_delegatee_#{ delegatee.to_s } self.#{ delegatee.to_s } ||= #{ classname }.new self.#{ delegatee.to_s } end EOS end before_save do |model| model.methods.grep( /^_fetch_delegatee_/ ).each do |mname| del = model.method( mname ).call del.save! end end end </CODE> Cheers again, Doug. -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---