Anatol Pomozov
2005-Dec-06 21:13 UTC
How to redefine find() method for ActiveRecord::Base??
Hi Railers. I am trying to add very simple caching system for ActiveRecord to my application and I need advice from Ruby (or RoR) guru. Everything that I need is redefine find method for some of my models. I am trying to do with folowing code class ActiveRecord::Base def self.cacheable class_eval <<-end_eval alias old__find find def self.find(*args) puts "We are in find" end end_eval end end Then I adding cacheable to my model. But when I run test I have following error C:/work/agora/agora/config/../lib/model_cachable.rb:3:in `module_eval'': (eval):2:in `module_eval'': undefined method `find'' for class `Category'' (NameError) from C:/work/agora/agora/config/../lib/model_cachable.rb:3:in `module_eval'' from C:/work/agora/agora/config/../lib/model_cachable.rb:3:in `cacheable'' from C:/work/agora/agora/config/../app/models/category.rb:2 from c:/progra~1/ruby/lib/ruby/gems/1.8/gems/activesupport-1.2.3/lib/active_support/dependencies.rb:207:in `load'' from c:/progra~1/ruby/lib/ruby/gems/1.8/gems/activesupport-1.2.3/lib/active_support/dependencies.rb:207:in `load'' from c:/progra~1/ruby/lib/ruby/gems/1.8/gems/activesupport-1.2.3/lib/active_support/dependencies.rb:39:in `require_or_load'' from c:/progra~1/ruby/lib/ruby/gems/1.8/gems/activesupport-1.2.3/lib/active_support/dependencies.rb:22:in `depend_on'' from c:/progra~1/ruby/lib/ruby/gems/1.8/gems/activesupport-1.2.3/lib/active_support/dependencies.rb:30:in `associate_with'' ... 35 levels... from C:/work/agora/agora/test/unit/../test_helper.rb:3 Seems that alias could not find function find(). Where it defined?? How could I do alias for it?? Probably there is other simplier way to overwrite find() method?? -- anatol (http://pomozov.info) _______________________________________________ Rails mailing list Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org http://lists.rubyonrails.org/mailman/listinfo/rails
Kent Sibilev
2005-Dec-06 22:04 UTC
Re: How to redefine find() method for ActiveRecord::Base??
In development mode, Rails reevaluates all your model classes. So your first call to cacheable method works, but the second gives you this exception since it lost the reference to the original AR::Base.find method. Try this class ActiveRecord::Base def self.cacheable return if singleton_methods.include? ''old__find'' class << self alias old__find find def find puts "We are in find" end end end end Kent. On Tuesday 06 December 2005 16:13, Anatol Pomozov wrote:> Hi Railers. > > I am trying to add very simple caching system for ActiveRecord to my > application and I need advice from Ruby (or RoR) guru. > Everything that I need is redefine find method for some of my models. I am > trying to do with folowing code > > class ActiveRecord::Base > def self.cacheable > class_eval <<-end_eval > alias old__find find > > def self.find(*args) > puts "We are in find" > end > end_eval > end > end > > Then I adding cacheable to my model. But when I run test I have following > error > > C:/work/agora/agora/config/../lib/model_cachable.rb:3:in `module_eval'': > (eval):2:in `module_eval'': undefined method `find'' for class `Category'' > (NameError) > from C:/work/agora/agora/config/../lib/model_cachable.rb:3:in > `module_eval'' > from C:/work/agora/agora/config/../lib/model_cachable.rb:3:in > `cacheable'' > from C:/work/agora/agora/config/../app/models/category.rb:2 > from > c:/progra~1/ruby/lib/ruby/gems/1.8/gems/activesupport-1.2.3/lib/active_supp >ort/dependencies.rb:207:in `load'' > from > c:/progra~1/ruby/lib/ruby/gems/1.8/gems/activesupport-1.2.3/lib/active_supp >ort/dependencies.rb:207:in `load'' > from > c:/progra~1/ruby/lib/ruby/gems/1.8/gems/activesupport-1.2.3/lib/active_supp >ort/dependencies.rb:39:in `require_or_load'' > from > c:/progra~1/ruby/lib/ruby/gems/1.8/gems/activesupport-1.2.3/lib/active_supp >ort/dependencies.rb:22:in `depend_on'' > from > c:/progra~1/ruby/lib/ruby/gems/1.8/gems/activesupport-1.2.3/lib/active_supp >ort/dependencies.rb:30:in `associate_with'' > ... 35 levels... > from C:/work/agora/agora/test/unit/../test_helper.rb:3 > > Seems that alias could not find function find(). Where it defined?? How > could I do alias for it?? Probably there is other simplier way to overwrite > find() method?? > > -- > anatol (http://pomozov.info)
Kent Sibilev
2005-Dec-06 22:05 UTC
Re: How to redefine find() method for ActiveRecord::Base??
In development mode, Rails reevaluates all your model classes. So your first call to cacheable method works, but the second gives you this exception since it lost the reference to the original AR::Base.find method. Try this class ActiveRecord::Base def self.cacheable return if singleton_methods.include? ''old__find'' class << self alias old__find find def find puts "We are in find" end end end end Kent. On Tuesday 06 December 2005 16:13, Anatol Pomozov wrote:> Hi Railers. > > I am trying to add very simple caching system for ActiveRecord to my > application and I need advice from Ruby (or RoR) guru. > Everything that I need is redefine find method for some of my models. I am > trying to do with folowing code > > class ActiveRecord::Base > def self.cacheable > class_eval <<-end_eval > alias old__find find > > def self.find(*args) > puts "We are in find" > end > end_eval > end > end > > Then I adding cacheable to my model. But when I run test I have following > error > > C:/work/agora/agora/config/../lib/model_cachable.rb:3:in `module_eval'': > (eval):2:in `module_eval'': undefined method `find'' for class `Category'' > (NameError) > from C:/work/agora/agora/config/../lib/model_cachable.rb:3:in > `module_eval'' > from C:/work/agora/agora/config/../lib/model_cachable.rb:3:in > `cacheable'' > from C:/work/agora/agora/config/../app/models/category.rb:2 > from > c:/progra~1/ruby/lib/ruby/gems/1.8/gems/activesupport-1.2.3/lib/active_supp >ort/dependencies.rb:207:in `load'' > from > c:/progra~1/ruby/lib/ruby/gems/1.8/gems/activesupport-1.2.3/lib/active_supp >ort/dependencies.rb:207:in `load'' > from > c:/progra~1/ruby/lib/ruby/gems/1.8/gems/activesupport-1.2.3/lib/active_supp >ort/dependencies.rb:39:in `require_or_load'' > from > c:/progra~1/ruby/lib/ruby/gems/1.8/gems/activesupport-1.2.3/lib/active_supp >ort/dependencies.rb:22:in `depend_on'' > from > c:/progra~1/ruby/lib/ruby/gems/1.8/gems/activesupport-1.2.3/lib/active_supp >ort/dependencies.rb:30:in `associate_with'' > ... 35 levels... > from C:/work/agora/agora/test/unit/../test_helper.rb:3 > > Seems that alias could not find function find(). Where it defined?? How > could I do alias for it?? Probably there is other simplier way to overwrite > find() method?? > > -- > anatol (http://pomozov.info)
Rick Olson
2005-Dec-06 22:42 UTC
Re: How to redefine find() method for ActiveRecord::Base??
> Seems that alias could not find function find(). Where it defined?? How > could I do alias for it?? Probably there is other simplier way to overwrite > find() method??There is no clean way to override find(). Due to the way it takes multiple types of parameters, it ends up calling itself. To counter this, I actually added a new option: def find(*args) options = extract_options_from_args!(args) call_original_find = lambda { original_find(*(args << options)) } if !options[:with_deleted] with_deleted_scope { return call_original_find.call } end call_original_find.call end def find_with_deleted(*args) original_find(*(args << extract_options_from_args!(args).merge(:with_deleted => true))) end The whole plugin can be viewed at http://techno-weenie.net/svn/projects/plugins/acts_as_paranoid/lib/acts_as_paranoid.rb As the author of a plugin that overrides find, I would suggest not doing it. It''s way more trouble than it''s worth. KIDS: Don''t try this at home -- rick http://techno-weenie.net
Anatol Pomozov
2005-Dec-08 17:06 UTC
Re: How to redefine find() method for ActiveRecord::Base??
Thanks everyone for the help. I redefine find() method but seems ActiveRecord does not intended for it. It is strange because there could be a lot of plugins that would require some proxying between user calls and db operations. act_as_paranoid one of them. The problem is that find() recursively calls find(). (See AR code). So when I call for example MyModel.find(3) it first calls my version of find, I am doing caching operations and then call original method (from AR). AR adds some params and also calls find() which is MINE version, and again I do some cache operation and call original find(). In my case it works (without any infinite recursion) but for complex cases it would be painful, I believe. One of the solutions could be adding to Ruby something like self() or this() which calls the same method, do recursion. But it would be problematic to ask Matz about it, I guess. Anyway there is very simple cache code that just adds cache for calls like MyModel.find(1) and MyModel.find("434"). It works for me. class ActiveRecord::Base def self.cacheable return if singleton_methods.include? ''__old__find'' module_eval <<-end_eval mattr_reader :cache @@cache = {} mattr_accessor :cache_hits @@cache_hits = 0 def self.flush_cache @@cache = {} @@cache_hits = 0 end end_eval class << self alias_method :__old__find, :find def find(*args) #Search only if there is only argument that number or string representing number if args.size == 1 arg1 = args[0] id case arg1 when Integer arg1 when String arg1.to_i else nil end if id and id != 0 if self.cache[id] self.cache_hits += 1 return self.cache[id] end #If we did not find it in cache try to select from db obj = __old__find(*args) if obj return self.cache[id] = obj end end end return __old__find(*args) end end end end On 12/6/05, Rick Olson <technoweenie-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> > > Seems that alias could not find function find(). Where it defined?? How > > could I do alias for it?? Probably there is other simplier way to > overwrite > > find() method?? > > There is no clean way to override find(). Due to the way it takes > multiple types of parameters, it ends up calling itself. To counter > this, I actually added a new option: > > def find(*args) > options = extract_options_from_args!(args) > call_original_find = lambda { original_find(*(args << options)) } > > if !options[:with_deleted] > with_deleted_scope { return call_original_find.call } > end > > call_original_find.call > end > > def find_with_deleted(*args) > original_find(*(args << > extract_options_from_args!(args).merge(:with_deleted => true))) > end > > The whole plugin can be viewed at > > http://techno-weenie.net/svn/projects/plugins/acts_as_paranoid/lib/acts_as_paranoid.rb > > As the author of a plugin that overrides find, I would suggest not > doing it. It''s way more trouble than it''s worth. > > KIDS: Don''t try this at home > > -- > rick > http://techno-weenie.net > _______________________________________________ > Rails mailing list > Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org > http://lists.rubyonrails.org/mailman/listinfo/rails >-- anatol (http://pomozov.info) _______________________________________________ Rails mailing list Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org http://lists.rubyonrails.org/mailman/listinfo/rails