I'm currently writing a plugin, which "could" be a patch in that it
must override a private method of ActiveRecord API, in
ActiveRecord::Dirty, to work smoothly. To make a long story short,
this plugin aims at enabling serialization with Marshal instead of
YAML (it actually works fine :)) -- the private update_with_dirty()
method, defined by an "alias_method_chain :update, :dirty", has to be
edited. Not really stable, but hey, it fits my needs
In my vendor/plugins/marshalize/lib/marshalize.rb, I defined a
Marshalization module, with some submodules which are included by
ActiveRecord::Base in vendor/plugins/marshalize/rails/init.rb using
send(). In the submodules I overrided several public class and
instance methods from several ActiveRecord submodules, all is fine.
But I did not manage to override the private method update_with_dirty
() from ActiveRecord::Dirty submodule directly from the plugin. Notice
thatthis method eventually becomes an instance private method of
ActiveRecord::Base since Dirty is "instance_evaled" by Base (at the
bottom of base.rb). For the sake of the tests, I created another
private method in my Marshalization::Dirty submodule and it eventually
gets included in ActiveRecord::Base as an instance private method; but
when it comes to using update_with_dirty(), Rails keeps using the old
one from the system active_record/lib/dirty.rb file and not the new
one defined in marshalize.rb -- and I don't figure out why
I'm currently writing a plugin, which "could" be a patch in that it
must override a private method of ActiveRecord API, in
ActiveRecord::Dirty, to work smoothly. To make a long story short,
this plugin aims at enabling serialization with Marshal instead of
YAML (it actually works fine :)) -- the private update_with_dirty()
(you 'll want to make sure that the column is a blob one rather than
text/string. Depending on database and column settings the database
might just truncate your data (eg if the column is marked as being
utf8 text but you try and store a byte sequence which is not legal
utf8.
method, defined by an "alias_method_chain :update, :dirty", has to be
edited. Not really stable, but hey, it fits my needs
In my vendor/plugins/marshalize/lib/marshalize.rb, I defined a
Marshalization module, with some submodules which are included by
ActiveRecord::Base in vendor/plugins/marshalize/rails/init.rb using
send(). In the submodules I overrided several public class and
instance methods from several ActiveRecord submodules, all is fine.
But I did not manage to override the private method update_with_dirty
() from ActiveRecord::Dirty submodule directly from the plugin. Notice
thatthis method eventually becomes an instance private method of
ActiveRecord::Base since Dirty is "instance_evaled" by Base (at the
bottom of base.rb). For the sake of the tests, I created another
private method in my Marshalization::Dirty submodule and it eventually
gets included in ActiveRecord::Base as an instance private method; but
when it comes to using update_with_dirty(), Rails keeps using the old
one from the system active_record/lib/dirty.rb file and not the new
one defined in marshalize.rb -- and I don't figure out why
Are you sure the problem is the privateness ? My hunch is that the
following happens
- update_with_dirty is defined
- that method is aliased to update
- you define a new update_with_dirty, but by then it's too late: the
aliasing has already happen
Hiha! 'did it, thanks to your hint. You were right, that was not
related to the privacy of the method. Ok, it was tricky, aliasing mess
inside. I added a file (test-overriding.rb) to my gist linked above to
demonstrate the behavior with a simple example that mimics
ActiveRecord structure. I also edited the plugin files, it eventually
looks like this in marshalize.rb:
module Marshalization
module Dirty #:nodoc:
private
def update_with_marshalization
if partial_updates?
# Serialized *and* marshalized attributes should always be
written in case they've been changed in place.
update_without_dirty(changed |
self.class.serialized_attributes.keys |
self.class.marshalized_attributes.keys)
else
update_without_dirty
end
end
def self.included(receiver)
receiver.alias_method_chain :update, :marshalization
end
end
# stuff...
end
So as you can see, it all relates to alias_method_chain whose behavior
is a bit tricky to grasp when it comes to submodules inheritance (at
least, to me). Hope that'll be useful to someone. One has to
alias_chain_method again the main (original) method. You cannot just
call update nor alias update_with_dirty, because alias_chain_method
has kind of broke the relationship into pieces (I mean, if you follow
the trace, it keeps referencing the original method through the
aliasing chain, but when it comes to coding your alias, that's the
original name you get access to).
As a matter of fact, it would be better to actually add the behavior
for marshalized_attributes only, then call super, but it's a private
method here, so one has to use send. Since send will be "broken" in
this kind of context with Ruby 1.9, I'd rather create a publicize
method to temporary make the super method public, yielding to it in
this context. Anyway, the main issue is solved ^^