Where is the best place to stash Ruby files that add functionality to Rails classes (in my case, ActiveRecord::Base)? I think my module is being loaded too late, and causing a class method to be undefined. Here''s what I tried: I wrote a module to add a new class method to ActiveRecord::Base following a pattern that I picked up from http://guides.rubyonrails.org/plugins.html; in my module implement the included callback and have it extend the target class with my class methods (actually singleton methods on the metaclass of my class object if I''m understanding all of this weirdness correctly): def self.included(base) base.send :extend, ClassMethods end module ClassMethods def my_method end end Then outside of the module definition I tell ActiveRecord::Base to include my module: ActiveRecord::Base.send(:include, MyModule) And according to my (obviously poorly written) unit test, this all worked wonderfully: def test_responds_to_my_method assert MyModel.respond_to?(:my_method, true) end Great! Except.. class MyModel < ActiveRecord::Base my_method # undefined local variable or method `my_method'' end So it seems reasonable to me that this might happen if my_model.rb is loaded first, then my module, then the test. Additional evidence is that if I add a "debugger" line to my included callback, I''m never sent to the debugger. Unless I remove the call to my_method, then I am. So if my hypothesis is correct, it''s a bad idea to add my modules to lib\modules and append that to the end of my load path. But then where should I stash modules that update rails classes?
Hi, On 28/05/2009, at 2:06 PM, Brian wrote:> > Where is the best place to stash Ruby files that add functionality to > Rails classes (in my case, ActiveRecord::Base)? I think my module is > being loaded too late, and causing a class method to be undefined. > > Here''s what I tried: I wrote a module to add a new class method to > ActiveRecord::Base following a pattern that I picked up from > http://guides.rubyonrails.org/plugins.html; in my module implement the > included callback and have it extend the target class with my class > methods (actually singleton methods on the metaclass of my class > object if I''m understanding all of this weirdness correctly): > > def self.included(base) > base.send :extend, ClassMethods > end > > module ClassMethods > def my_method > end > end >This is not a class method, is it? it''s an instance method. a class method would be def self.my_method end> Then outside of the module definition I tell ActiveRecord::Base to > include my module: > > ActiveRecord::Base.send(:include, MyModule) > > And according to my (obviously poorly written) unit test, this all > worked wonderfully: > > def test_responds_to_my_method > assert MyModel.respond_to?(:my_method, true) > end > > Great! Except.. > > class MyModel < ActiveRecord::Base > my_method # undefined local variable or method `my_method'' > end >I don''t follow what you want to do here... you''re calling the method inside the class definition. That makes no sense. What did you want to happen? In other words, you''re running a method WHILE declaring the class. Perhaps you wanted something like this: class MyModel < ActiveRecord::Base def some_other_method my_method end end that will work, but my_method is still here not a class method... it''s an instance method... (as far as my understanding goes) because of the way the module is defined. ---------------------------------------------- Learn: http://sensei.zenunit.com/ Last updated 20-May-09 (Rails, Basic Unix) Blog: http://random8.zenunit.com/ Twitter: http://twitter.com/random8r> So it seems reasonable to me that this might happen if my_model.rb is > loaded first, then my module, then the test. Additional evidence is > that if I add a "debugger" line to my included callback, I''m never > sent to the debugger. Unless I remove the call to my_method, then I > am. > > So if my hypothesis is correct, it''s a bad idea to add my modules to > lib\modules and append that to the end of my load path. But then > where should I stash modules that update rails classes? > > > > >
On May 28, 5:06 am, Brian <butler.bria...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> So it seems reasonable to me that this might happen if my_model.rb is > loaded first, then my module, then the test. Additional evidence is > that if I add a "debugger" line to my included callback, I''m never > sent to the debugger. Unless I remove the call to my_method, then I > am. > > So if my hypothesis is correct, it''s a bad idea to add my modules to > lib\modules and append that to the end of my load path. But then > where should I stash modules that update rails classes?The problem is not so much where you put it but when it''s loaded. If you just stick a file in lib/blah rails isn''t going to magically trip over it. It will load your module if elsewhere you say MyModule, and in production mode it will load it at some point. So you need to require it explicitly and you need to do so at the right point, and that happens to be from an initializer (ie a file in config/ initializers) - these are run after rails has been loaded, but before your application classes are. Fred
On May 28, 7:59 am, Julian Leviston <jul...-AfxEtdRqmE/tt0EhB6fy4g@public.gmane.org> wrote:> > I don''t follow what you want to do here... you''re calling the method > inside the class definition. That makes no sense. What did you want to > happen? > In other words, you''re running a method WHILE declaring the class.That''s reasonable enough. Just like acts_as_list, has_many, etc... Fred
Thanks, Fred! This works as expected now. I''ve made a config/initializers/modules.rb. For now I only require this one module, but I''ll probably just have this require everything in lib/modules so that I don''t have to worry about the next one. On May 28, 4:13 am, Frederick Cheung <frederick.che...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> On May 28, 5:06 am, Brian <butler.bria...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote: > > > So it seems reasonable to me that this might happen if my_model.rb is > > loaded first, then my module, then the test. Additional evidence is > > that if I add a "debugger" line to my included callback, I''m never > > sent to the debugger. Unless I remove the call to my_method, then I > > am. > > > So if my hypothesis is correct, it''s a bad idea to add my modules to > > lib\modules and append that to the end of my load path. But then > > where should I stash modules that update rails classes? > > The problem is not so much where you put it but when it''s loaded. If > you just stick a file in lib/blah rails isn''t going to magically trip > over it. It will load your module if elsewhere you say MyModule, and > in production mode it will load it at some point. So you need to > require it explicitly and you need to do so at the right point, and > that happens to be from an initializer (ie a file in config/ > initializers) - these are run after rails has been loaded, but before > your application classes are. > > Fred
On May 28, 2:59 am, Julian Leviston <jul...-AfxEtdRqmE/tt0EhB6fy4g@public.gmane.org> wrote:> Hi, > > > def self.included(base) > > base.send :extend, ClassMethods > > end > > > module ClassMethods > > def my_method > > end > > end > > This is not a class method, is it? it''s an instance method. a class > method would be > > def self.my_method > end > >I have all of 20 hours of Ruby experience now, so I''ll probably screw up this explanation. The whole include/extend thing is still very confusing to me, but I''m having ActiveRecord::Base call include on my module and attaching a callback so that I can do more. ActiveRecord::Base is an instance of the Class class, so when the callback uses extend, it is the ActiveRecord::Base instance itself (and ActiveRecord::Base objects) that pick up the new my_method instance method. My understanding is that Ruby then attaches this to a metaclass for the ActiveRecord::Base instance, making it a singleton method. And I think that''s all class methods really are; singleton methods on the metadata class for an instance of the Class class. So through module trickery I have created a new class method. If I have this wrong, let me know. I don''t like to write code that I don''t understand, so the sooner I wrap my head around the actual mechanics of how this works, the better.> > I don''t follow what you want to do here... you''re calling the method > inside the class definition. That makes no sense. What did you want to > happen? > In other words, you''re running a method WHILE declaring the class. >For any language other than ruby, I would agree that this doesn''t make sence. :) Apparently it''s standard practice though for this language. You can run methods right in the middle of your class definition. You can try this out pretty easily in irb: irb(main):001:0> class A irb(main):002:1> puts "hello!" irb(main):003:1> end hello! => nil Strange! But useful. In my case I''m actually planning to create three class methods similar to my_method above. When run these will generate instance methods for my models (similar to associations).
On Thu, May 28, 2009 at 10:02 AM, Brian <butler.brian.j-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> > On May 28, 2:59 am, Julian Leviston <jul...-AfxEtdRqmE/tt0EhB6fy4g@public.gmane.org> wrote: >> Hi, >> >> > def self.included(base) >> > base.send :extend, ClassMethods >> > end >> >> > module ClassMethods >> > def my_method >> > end >> > end >> >> This is not a class method, is it? it''s an instance method. a class >> method would be >> >> def self.my_method >> end >> >> > > I have all of 20 hours of Ruby experience now, so I''ll probably screw > up this explanation. The whole include/extend thing is still very > confusing to me, but I''m having ActiveRecord::Base call include on my > module and attaching a callback so that I can do more. > ActiveRecord::Base is an instance of the Class class, so when the > callback uses extend, it is the ActiveRecord::Base instance itself > (and ActiveRecord::Base objects) that pick up the new my_method > instance method. My understanding is that Ruby then attaches this to > a metaclass for the ActiveRecord::Base instance, making it a singleton > method. And I think that''s all class methods really are; singleton > methods on the metadata class for an instance of the Class class. So > through module trickery I have created a new class method. > > If I have this wrong, let me know. I don''t like to write code that I > don''t understand, so the sooner I wrap my head around the actual > mechanics of how this works, the better.No, you have this quite right, and extending a class/module with a ''class methods'' module inside another module using the included hook is the standard way to add class methods. -- Rick DeNatale Blog: http://talklikeaduck.denhaven2.com/ Twitter: http://twitter.com/RickDeNatale WWR: http://www.workingwithrails.com/person/9021-rick-denatale LinkedIn: http://www.linkedin.com/in/rickdenatale