Monkey Patching 2 Methods, Overrides One Method, Not The Other

I’m monkey patching 2 methods of an existing module: some_method() and
another_method()

Calling some_method() on an instance of class that includes the
original module works fine (calls the monkey patched version), but
when I call another_method() the original (not the monkey patched)
method is called.

There is a lot of code, and I’ve tried to recreate the series of
includes with the below example, yet the example works!

Given this, I hope that someone could still provide me with some
insight as to why this would happen

#original_module.rb

module ToBePatched
module A
def some_method
p ‘original some’
end

def another_method
  p 'original anotehr'
end

end
end

#original_class.rb

module ToBePatched
class Foo
include ToBePatched::A
end
end

#my_override.rb

module MyModule
module MyNestedModule
def bar
p ‘Bar’
end
end
end

module ToBePatched
module A
alias_method :old_some_method, :some_method
def some_method
p ‘override some’
old_some_method
end

alias_method :old_another_method, :another_method
def another_method
  p 'override another'
  old_another_method
end
#override other methods

end
end

#my_main.rb

module MyModule
module Main
def self.included(base)
base.extend ClassMethods
end

module ClassMethods
  def  i_want_that_great_feature

class_eval do
include MyModule::MyNestedModule
end
end
end
end
end

#test program

ToBePatched::Foo.class_eval do
include MyModule::Main
end

class Bs < ToBePatched::Foo
i_want_that_great_feature
end

bs = Bs.new
bs.bar
bs.some_method
bs.another_method

MaggotChild wrote:

#test program

ToBePatched::Foo.class_eval do
include MyModule::Main
end

class Bs < ToBePatched::Foo
i_want_that_great_feature
end

bs = Bs.new
bs.bar
bs.some_method
bs.another_method

$ ruby r1test.rb
r1test.rb:1: uninitialized constant ToBePatched (NameError)

On Sep 16, 10:51 pm, 7stud – [email protected] wrote:

bs = Bs.new
bs.bar
bs.some_method
bs.another_method

$ ruby r1test.rb
r1test.rb:1: uninitialized constant ToBePatched (NameError)

Not sure what’s in r1test.rb, my above code does indeed run.
Regardless, that example is just a simplified version of the monkey
patching paradigm that’s causing problems.

What I’m interested in is why would one overridden method in a module
run while another would fall back to the original as if it were never
overridden?

Hi –

On Thu, 17 Sep 2009, MaggotChild wrote:

Given this, I hope that someone could still provide me with some
insight as to why this would happen

I’m never quite sure what people mean by “monkey patching” – it
always sounds like deliberately doing something badly, so that having
it not work shouldn’t be a surprise :slight_smile: – but I think I’d have to see
code that exhibits the problem you’re describing in order to analyze
it. Can you look at the difference between your real code and your
example, and try to spot what’s not working?

David

I’d seriously reconsider that design… looks very obfuscated to me.
Maybe restructuring your code and untangling that series of
includes will solve your problem or at least expose it.

I’ve made the experience that most of the time there is a nicer way
than class-evaling stuff and including modules that automatically
include other modules (although probably not always).

It always helps me to sketch my current code out on a board or
a large sheet of paper and then untangle it.
Most of the time there is a really rather good OO solution that
doesn’t need any “big magic” behind the scenes.

As David says, it’s really rather difficult to give you any other advice
unless
you give us some of your real code.

Greetz!

2009/9/17 David A. Black [email protected]

On Sep 17, 5:02 am, “David A. Black” [email protected] wrote:

There is a lot of code, and I’ve tried to recreate the series of
includes with the below example, yet the example works!

Given this, I hope that someone could still provide me with some
insight as to why this would happen

I’m never quite sure what people mean by “monkey patching”
– it always sounds like deliberately doing something badly, so that having
it not work shouldn’t be a surprise :slight_smile:

It sure sounds like you understand what I mean, and I agree, yet the
problematic modules (mine and the one I’m attempting to override) are
filled with this sort of behavior and a full rewrite is not feasible
at this time.

It’s unfortunate that the Ruby community has adopted such an abhorrent
practice to the extent it has.
Furthermore, what sort of examples are we setting for the children who
take up Ruby as their first language.

I hope that the The Well-Grounded Rubyist isn’t advocating this sort
of OO flummory :^)

Can you look at the difference between your real code and your
example, and try to spot what’s not working?

I’d like to think that it has something to do with the way the modules
are required. In the real code there are far more requires and even
a load.

I go back to my generalization of this problem: How can one module
revert back to its original implementation on one method and not the
other, when both the original and overridden methods are contained in
1 file respectively?

Thanks

On Sep 17, 10:38 am, Fabian S. [email protected]
wrote:

[Note: parts of this message were removed to make it a legal post.]

I’d seriously reconsider that design… looks very obfuscated to me.
Maybe restructuring your code and untangling that series of
includes will solve your problem or at least expose it.

I’ve made the experience that most of the time there is a nicer way
than class-evaling stuff and including modules that automatically
include other modules (although probably not always).

Yes I agree, it’s horrible. Unfortunately it’s part of a much larger
code base that I can’t rewrite overnight.

Hi –

On Fri, 18 Sep 2009, MaggotChild wrote:

method is called.

I hope that the The Well-Grounded Rubyist isn’t advocating this sort
of OO flummory :^)

I think we might be talking at cross-purposes. I dislike the term
“monkey patching”, partly because I never know quite what it means and
partly because, whatever else it means, it stigmatizes practices that
are, in their place, perfectly fine. It’s not a criticism of your
code. (I haven’t even seen your code :slight_smile:

1 file respectively?
Ceteris paribus, it can’t. So something’s up that won’t be duplicable
in the stripped-down version. One suggestion is to think of it from
the object’s perspective: it’s not that the module reverts to a former
implementation, but that the old version has someone been positioned
before the new one in the object’s search path. If you track down
exactly what class/module path the object is walking, you might be
able to spot the difference.

David

I’m never quite sure what people mean by “monkey patching” – it
always sounds like deliberately doing something badly, so that
having it not work shouldn’t be a surprise :slight_smile:

I am glad Dave is hitting on that so I don’t have to!
I got so downvoted on reddit when I tried to replace the term
“monkey patching” with “god patching” - guess I couldn’t quite
convince people that “god patching” would be a more appropriate
name … :wink:

But hey - I am just glad that I don’t seem to be the only one with
a slight dislike for the term “monkey patching”. Poor monkeys, too!

why is there two threads on this topic?
If you look in the other thread, there’s a bunch of
code missing. If you add that, it works.

Greetz!

2009/9/18 7stud – [email protected]

MaggotChild wrote:

On Sep 16, 10:51�pm, 7stud – [email protected] wrote:

bs = Bs.new
bs.bar
bs.some_method
bs.another_method

$ ruby r1test.rb
r1test.rb:1: uninitialized constant ToBePatched (NameError)

Not sure what’s in r1test.rb,

Everything that I quoted.

On Thu, Sep 17, 2009 at 4:22 PM, Marc H. [email protected]
wrote:

But hey - I am just glad that I don’t seem to be the only one with
a slight dislike for the term “monkey patching”. Poor monkeys, too!

Posted via http://www.ruby-forum.com/.

Monkey patching is supposed to be a euphemism for guerrilla patching,
now
the euphemism needs a euphemism, lol.

Hi –

On Fri, 18 Sep 2009, Josh C. wrote:

name … :wink:

But hey - I am just glad that I don’t seem to be the only one with
a slight dislike for the term “monkey patching”. Poor monkeys, too!

Posted via http://www.ruby-forum.com/.

Monkey patching is supposed to be a euphemism for guerrilla patching, now
the euphemism needs a euphemism, lol.

I’m boring, so I like saying “overriding methods”, “reopening
classes”, “programming”, and things like that :slight_smile:

David

maybe you could try rdebug and see in what order the important
methods are called. Just insert a breakpoint into all of them.
Maybe the order of calls got confused somewhere?

Greetz!

But hey - I am just glad that I don’t seem to be the only one with

a slight dislike for the term “monkey patching”. Poor monkeys, too!

Same goes for duck typing… those ducks didn’t hurt anyone! qwack

“programming”, and things like that :slight_smile:

That word is way over-used. I think we should call it puppy-coding
or maybe llama-cracking…

On Sep 17, 5:02 am, “David A. Black” [email protected] wrote:

I think I’d have to see
code that exhibits the problem you’re describing in order to analyze

This is suited for the Rails group, but the problem can be seen in its
simplest form here:

#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”

Hi –

On Thu, 24 Sep 2009, MaggotChild wrote:

module AttributeMethods
end

person.read_attribute :name
“read_override”
=> “Bilal”

That’s very interesting. I have a hunch it must have to do with
autoloading… it has the flavor of what would happen if the original
file were re-loaded after the redefinition had already taken place but
before write_attribute is called. I don’t know why that would be, and
I don’t think I’ve nailed it, but I’ll keep looking.

David

Hi –

On Thu, 24 Sep 2009, MaggotChild wrote:

module AttributeMethods
end

person.read_attribute :name
“read_override”
=> “Bilal”

OK, here’s what’s happening.

On startup, ActiveRecord::Base mixes in a module called Dirty. That
module includes the instruction:

 def self.included(base)
   # ...
   base.alias_method_chain :write_attribute, :dirty
   # ...
 end

At that point, write_attribute is now an instance method of
ActiveRecord::Base – and your initializer has not yet been run. That
means that when you run the initializer and make the change in the
AttributesMethod module, it’s too late: write_attribute has been
copied and put directly in the class.

Of course you can change it in the initializer, if you open AR::Base.
But anyway, that’s why the change in the module is not having any
effect.

David