MaggotChild
2009-Sep-23 22:27 UTC
Overriding AR read/write_attribute - Overridden write_attribute Is Never Called
Could someone explain this? #config/initializers/ar_attributes.rb module ActiveRecord module AttributeMethods alias_method :ar_read_attribute, :read_attribute def read_attribute(attr_name) p "read_override" ar_read_attribute(attr_name) end alias_method :ar_write_attribute, :write_attribute def write_attribute(attr_name, value) raise ''You made it!'' end end end In the Rails console:>> person.read_attribute :name"read_override" => "Joe">> person.write_attribute :name, "Bilal"=> "Bilal">> person.read_attribute :name"read_override" => "Bilal"
Frederick Cheung
2009-Sep-24 07:34 UTC
Re: Overriding AR read/write_attribute - Overridden write_attribute Is Never Called
On Sep 23, 11:27 pm, MaggotChild <hsomob1...-/E1597aS9LQAvxtiuMwx3w@public.gmane.org> wrote:> Could someone explain this? >At a quick glance it is probably because after AttributeMethods is included in ActiveRecord::Base, write_attribute is aliased & overwridden (eg the change tracking module). You then change write_attribute on AttributeMethods but it is too late - the aliasing that occured in Dirty is pointing at the previous implementation. When you alias a method ruby does keep track of what the aliased method was at the time alias_method was called, for example: class Foo def to_be_aliased "implementation 1" end alias_method :old_implementation, :to_be_aliased end class Foo def to_be_aliased "implementation 2" end end Foo.new.old_implementation #=> "implementation 1" Fred> #config/initializers/ar_attributes.rb > > module ActiveRecord > module AttributeMethods > > alias_method :ar_read_attribute, :read_attribute > def read_attribute(attr_name) > p "read_override" > ar_read_attribute(attr_name) > end > > alias_method :ar_write_attribute, :write_attribute > def write_attribute(attr_name, value) > raise ''You made it!'' > end > end > end > > In the Rails console: > > >> person.read_attribute :name > > "read_override" > => "Joe" > > >> person.write_attribute :name, "Bilal" > => "Bilal" > >> person.read_attribute :name > > "read_override" > => "Bilal"
MaggotChild
2009-Sep-25 00:40 UTC
Re: Overriding AR read/write_attribute - Overridden write_attribute Is Never Called
On Sep 24, 12:34 am, Frederick Cheung <frederick.che...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> On Sep 23, 11:27 pm, MaggotChild <hsomob1...-/E1597aS9LQAvxtiuMwx3w@public.gmane.org> wrote: > > > Could someone explain this? > > At a quick glance it is probably because after AttributeMethods is > included in ActiveRecord::Base, write_attribute is aliased & > overwridden (eg the change tracking module). You then change > write_attribute on AttributeMethods but it is too late - the aliasing > that occured in Dirty is pointing at the previous implementation. When > you alias a method ruby does keep track of what the aliased method was > at the time alias_method was called, for example: > > class Foo > def to_be_aliased > "implementation 1" > end > alias_method :old_implementation, :to_be_aliased > end > > class Foo > def to_be_aliased > "implementation 2" > end > end > > Foo.new.old_implementation #=> "implementation 1"Hi Fred, Thanks for your response. I think you''re on to something and I''ll have to take a look a the Dirty module (amongst others). But, even if the module was aliasing the original write_attribute, I don''t see how this would interfere with my redefinition. write_attribute is at the top of the call stack so I''d think it''s going to use the most recent definition. Unless some other module redefined write_attribute within the scope of active AR::Base (as apposed to including it via a module) -which is possible. Using your example, I believe the case is more like the following: class Foo def to_be_aliased p "implementation 1" end alias_method :old_implementation, :to_be_aliased end class Foo def to_be_aliased p "implementation 2" end end Foo.new.to_be_aliased #implementation 2
David A. Black
2009-Sep-25 01:00 UTC
Re: Overriding AR read/write_attribute - Overridden write_attribute Is Never Called
Hi -- On Thu, 24 Sep 2009, Frederick Cheung wrote:> > > > On Sep 23, 11:27 pm, MaggotChild <hsomob1...-/E1597aS9LQAvxtiuMwx3w@public.gmane.org> wrote: >> Could someone explain this? >> > > At a quick glance it is probably because after AttributeMethods is > included in ActiveRecord::Base, write_attribute is aliased & > overwridden (eg the change tracking module). You then change > write_attribute on AttributeMethods but it is too late - the aliasing > that occured in Dirty is pointing at the previous implementation.This thread seems to be going on both here and in ruby-talk. See my most recent answer in ruby-talk; there is, indeed an alias_method_chain call involving write_attribute (but not read_attribute) that happens before the initializer is executed. David -- David A. Black, Director Ruby Power and Light, LLC (http://www.rubypal.com) Ruby/Rails training, consulting, mentoring, code review Book: The Well-Grounded Rubyist (http://www.manning.com/black2)
MaggotChild
2009-Sep-25 01:16 UTC
Re: Overriding AR read/write_attribute - Overridden write_attribute Is Never Called
On Sep 24, 5:40 pm, MaggotChild <hsomob1...-/E1597aS9LQAvxtiuMwx3w@public.gmane.org> wrote:> write_attribute is at the top of the call stack so I''d think it''s > going to use the most recent definition. Unless some other module > redefined write_attribute within the scope of active AR::Base (as > apposed to including it via a module) -which is possible.Yes, this is what happens, the alias is performed in a class_eval block giving it presidency over my attempted override in the module.
David A. Black
2009-Sep-25 01:17 UTC
Re: Overriding AR read/write_attribute - Overridden write_attribute Is Never Called
Hi -- On Thu, 24 Sep 2009, MaggotChild wrote:> > > > On Sep 24, 12:34 am, Frederick Cheung <frederick.che...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> > wrote: >> On Sep 23, 11:27 pm, MaggotChild <hsomob1...-/E1597aS9LQAvxtiuMwx3w@public.gmane.org> wrote: >> >>> Could someone explain this? >> >> At a quick glance it is probably because after AttributeMethods is >> included in ActiveRecord::Base, write_attribute is aliased & >> overwridden (eg the change tracking module). You then change >> write_attribute on AttributeMethods but it is too late - the aliasing >> that occured in Dirty is pointing at the previous implementation. When >> you alias a method ruby does keep track of what the aliased method was >> at the time alias_method was called, for example: >> >> class Foo >> def to_be_aliased >> "implementation 1" >> end >> alias_method :old_implementation, :to_be_aliased >> end >> >> class Foo >> def to_be_aliased >> "implementation 2" >> end >> end >> >> Foo.new.old_implementation #=> "implementation 1" > > > Hi Fred, > > Thanks for your response. I think you''re on to something and I''ll have > to take a look a the Dirty module (amongst others). But, even if the > module was aliasing the original write_attribute, I don''t see how this > would interfere with my redefinition. > > write_attribute is at the top of the call stack so I''d think it''s > going to use the most recent definition. Unless some other module > redefined write_attribute within the scope of active AR::Base (as > apposed to including it via a module) -which is possible. > > > Using your example, I believe the case is more like the following: > > class Foo > def to_be_aliased > p "implementation 1" > end > alias_method :old_implementation, :to_be_aliased > end > > class Foo > def to_be_aliased > p "implementation 2" > end > end > > Foo.new.to_be_aliased #implementation 2It involves alias_method_chain, so it''s somewhat like this: module AR module M def x; puts "M#x"; end def self.included(base) base.alias_method_chain(:x, :y) end end class Base def x_with_y; puts "x_with_y"; end p Base.instance_methods(false).sort # does not include "x" include M p Base.instance_methods(false).sort # does include "x" end end AR::Base.new.x After alias_method_chain, AR::Base has an "x" method, so overriding the one in AR::M won''t affect what happens when you call x. I stripped it down to the bare bones (and then some, perhaps :-) but I think this represents what happens. See the file dirty.rb, which is where the alias_method_chain call is. David -- David A. Black, Director Ruby Power and Light, LLC (http://www.rubypal.com) Ruby/Rails training, consulting, mentoring, code review Book: The Well-Grounded Rubyist (http://www.manning.com/black2)