Preferred monkeypatching technique

I think the term "monkypatching" as applied to this context (the
ability to re-open existing classes in Ruby and define new methods (or
redefine/undefine/alias existing methods) began in a Pythonista blog
that was criticizing Ruby because this sort of thing is possible and
done fairly frequently.

Now it would appear that people are willing to wear this term 'as a badge'.

Did 'duck typing' have similar origins :wink:

Phil

···

On 7/13/06, Hal Fulton <hal9000@hypermetrics.com> wrote:

dblack@wobblini.net wrote:
>
> I don't know whether it qualifies as "monkeypatching" (I always
> thought that meant doing something sloppy and ill-advised, which I
> hope I'm not :slight_smile: but see if this helps:

Actually it was only last week that I first saw this term
being used in the Ruby community. I wonder if it's too
late to squash it?

Can you give a concrete example where this takes place?

···

On Jul 13, 2006, at 5:04 PM, Tom Werner wrote:

Eric Hodel wrote:

You just described subclasses:

Well, the idea is that the 3rd party software is going to be using whatever classes it has always used, so just creating a subclass doesn't get me anywhere because the 3rd party app (i.e. Rails) doesn't give a hoot about my BigNozzleFiretruck.

--
Eric Hodel - drbrain@segment7.net - http://blog.segment7.net
This implementation is HODEL-HASH-9600 compliant

http://trackmap.robotcoop.com

You don't need a mutex.

class Symbol
   @gensym_count = 0
   def self.gensym
     ("__gensym__%X" % @gensym_count += 1).intern
   end
end

···

On Jul 14, 2006, at 10:02 AM, Logan Capaldo wrote:

Neat idea, gensym in Ruby. I would suggest making it thread-safe though:

% cat gensym.rb
require 'mutex'
class Symbol
  GensymLock = Mutex.new
  def self.gensym
    GensymLock.synchronize {
      @@gensym_count ||= 0
      @@gensym_count += 1
      ("__gensym__%X" % @@gensym_count).intern
    }
  end
end

--
Eric Hodel - drbrain@segment7.net - http://blog.segment7.net
This implementation is HODEL-HASH-9600 compliant

http://trackmap.robotcoop.com

I can't remember if I've posted this before, but here is my "poor
man's cut" which might be handy in the meantime:

def cut(klass, &block)
  klass = Object.send(:remove_const, klass.to_s)
  Object.const_set(klass.to_s, Class.new(klass, &block))
end

class A
  def hello
    "hello"
  end
end

a = A.new
puts a.hello

cut A do
  def hello
    "{" + super + "}"
  end
end

b = A.new
puts b.hello

# but beware - it only works on instances created ~after~ the cut
puts a.hello

__END__
hello
{hello}
hello

Regards,
Sean

···

On 7/14/06, Tom Werner <tom@helmetstohardhats.org> wrote:

Daniel Schierbeck wrote:
>
> Not a solution, but:
>
> <http://rcrchive.net/rcr/show/321&gt;
>
> cut MyCut < Firetruck
> def put_out_fire(options = {})
> super(options.merge(:nozzle => :big))
> end
> end
>
> (Trans, can I omit the `MyCut <' part?)
>
> Cheers,
> Daniel
>

I looked at that RCR a while back, but only glanced over it as it was
not applicable to my situation at the time. Looking at it again, I see
the beauty and power in it. I would love to see that end up in 2.0!

Tom

--

Tom Werner wrote:

Daniel Schierbeck wrote:

Tom Werner wrote:

Allow me to present a scenario:

class Firetruck
   def put_out_fire(options = {})
      # code
   end
end

Pretend Firetruck is in a 3rd party application (like Rails) that is happy to allow plugins to modify core code. Now, let's say I want to write some code that always adds a certain attribute to the options hash. I could do this:

class Firetruck
   alias_method :__old_put_out_fire, :put_out_fire
   def put_out_fire(options = {})
      __old_put_out_fire(options.merge({:nozzle => :big}))
   end
end

Which works just fine until someone else comes up with a plugin that wants to modify the same method (doing something similar to me) and just so happens to also use :__old_put_out_fire as THEIR alias. Now we've got my plugin's method as the alias calling itself, which leads to, you know, badness.

So I'm wondering if there's a better way. Perhaps some way to turn Firetruck into an ancestor of itself, so to speak, so that my plugin would create a new Firetruck class, pushing the old Firetruck backward in the chain and allowing me to call super instead and preventing alias_method explosions. Or would that just end up causing more havoc?

Not a solution, but:

  <http://rcrchive.net/rcr/show/321&gt;

  cut MyCut < Firetruck
    def put_out_fire(options = {})
      super(options.merge(:nozzle => :big))
    end
  end

(Trans, can I omit the `MyCut <' part?)

Cheers,
Daniel

I looked at that RCR a while back, but only glanced over it as it was not applicable to my situation at the time. Looking at it again, I see the beauty and power in it. I would love to see that end up in 2.0!

+1

transfire@gmail.com wrote:

Daniel Schierbeck wrote:

Tom Werner wrote:

Allow me to present a scenario:

class Firetruck
   def put_out_fire(options = {})
      # code
   end
end

Pretend Firetruck is in a 3rd party application (like Rails) that is
happy to allow plugins to modify core code. Now, let's say I want to
write some code that always adds a certain attribute to the options
hash. I could do this:

class Firetruck
   alias_method :__old_put_out_fire, :put_out_fire
   def put_out_fire(options = {})
      __old_put_out_fire(options.merge({:nozzle => :big}))
   end
end

Which works just fine until someone else comes up with a plugin that
wants to modify the same method (doing something similar to me) and just
so happens to also use :__old_put_out_fire as THEIR alias. Now we've got
my plugin's method as the alias calling itself, which leads to, you
know, badness.

So I'm wondering if there's a better way. Perhaps some way to turn
Firetruck into an ancestor of itself, so to speak, so that my plugin
would create a new Firetruck class, pushing the old Firetruck backward
in the chain and allowing me to call super instead and preventing
alias_method explosions. Or would that just end up causing more havoc?

Not a solution, but:

   <http://rcrchive.net/rcr/show/321&gt;

   cut MyCut < Firetruck
     def put_out_fire(options = {})
       super(options.merge(:nozzle => :big))
     end
   end

(Trans, can I omit the `MyCut <' part?)

Sure. Although, the name provides a handle if you need to dynamically
effect the cut later --just like any Ruby class. But you can of course
do it annonymously too:

  Cut.new(Firetruck) do
    ...
  end

Neat-o.

How about a method-level shorthand?

   class Firetruck
     redef put_out_fire(options = {})
       # maybe something other than "super"?
       super(options.merge(:nozzle => :big))
     end
   end

That way you can also easily cut a class from within itself, even conditionally.

Cheers,
Daniel

Hal Fulton wrote:

I don't know whether it qualifies as "monkeypatching" (I always
thought that meant doing something sloppy and ill-advised, which I
hope I'm not :slight_smile: but see if this helps:

Actually it was only last week that I first saw this term
being used in the Ruby community. I wonder if it's too
late to squash it?

Let's hope not.

I believe it comes from the Python community. Python (I think) allows for something similar to Ruby's ability to modify any and all classes, but my (limited) understanding is that there are enough differences between the two that what is reasonable and proper in Ruby is not quite so elegant or appropriate in Python. The term may be correct for Python, but in Ruby this is just a normal and correct way to use the language.

The use of the term indicates unease with, and lack of understanding of, Ruby on the part of the speaker, rather than describing anything about Ruby.

···

dblack@wobblini.net wrote:

--
James Britt

"In physics the truth is rarely perfectly clear, and that is certainly
  universally the case in human affairs. Hence, what is not surrounded by
  uncertainty cannot be the truth."
  - R. Feynman

Hi --

>
> I don't know whether it qualifies as "monkeypatching" (I always
> thought that meant doing something sloppy and ill-advised, which I
> hope I'm not :slight_smile: but see if this helps:

Actually it was only last week that I first saw this term
being used in the Ruby community. I wonder if it's too
late to squash it?

I think the term "monkypatching" as applied to this context (the
ability to re-open existing classes in Ruby and define new methods (or
redefine/undefine/alias existing methods) began in a Pythonista blog
that was criticizing Ruby because this sort of thing is possible and
done fairly frequently.

Now it would appear that people are willing to wear this term 'as a badge'.

The badge looks an awful lot like the criticism.... :slight_smile:

Did 'duck typing' have similar origins :wink:

No, that was introduced as is by Dave Thomas to characterize an
approach to Ruby programming that sought harmony with the dynamic
conditions inherent in Ruby.

As for "monkeypatching", I'm actually rooting to go in the opposite
direction: to reach a point where we don't even perceive a difference
between programming and "metaprogramming". That seems to me to be
what's on offer from Ruby.

David

···

On Fri, 14 Jul 2006, Phil Tomson wrote:

On 7/13/06, Hal Fulton <hal9000@hypermetrics.com> wrote:

dblack@wobblini.net wrote:

--
http://www.rubypowerandlight.com => Ruby/Rails training & consultancy
Ruby for Rails => RUBY FOR RAILS (reviewed on
                                     Slashdot, 7/12/2006!)
http://dablog.rubypal.com => D[avid ]A[. ]B[lack's][ Web]log
dblack@wobblini.net => me

Eric Hodel wrote:

Eric Hodel wrote:

You just described subclasses:

Well, the idea is that the 3rd party software is going to be using whatever classes it has always used, so just creating a subclass doesn't get me anywhere because the 3rd party app (i.e. Rails) doesn't give a hoot about my BigNozzleFiretruck.

Can you give a concrete example where this takes place?

My specific purpose is to write a plugin for Rails that adds an option to each form field helper (the ones that create <input> tags) that specifies an appropriate class (since CSS attribute selector support is lacking in IE). So I'm using code like this:

module ActionView
  module Helpers
    module FormHelper

      # text
      alias_method :__classless_text_field, :text_field
      def text_field(object_name, method, options = {})
        __classless_text_field(object_name, method, options.merge({:class => (options[:class].to_s + ' text').strip}))
      end

    end
  end
end

Which works fine, but is subject to the infinite loop problem should someone else happen to alias in an identical manner. Even the *possibility* of this happening makes me feel dirty, and so I'm looking for a cleaner way to do it. As these helpers may be called quite often, a solution that doesn't badly impact performance is essential. There are some other ideas on this thread that I still need to investigate, I was just curious if there's some standard practice for this sort of thing. Thanks!!

Tom

···

On Jul 13, 2006, at 5:04 PM, Tom Werner wrote:

--
Tom Werner
Helmets to Hardhats
Software Developer
tom@helmetstohardhats.org
www.helmetstohardhats.org

Neat idea, gensym in Ruby. I would suggest making it thread-safe though:

% cat gensym.rb
require 'mutex'
class Symbol
  GensymLock = Mutex.new
  def self.gensym
    GensymLock.synchronize {
      @@gensym_count ||= 0
      @@gensym_count += 1
      ("__gensym__%X" % @@gensym_count).intern
    }
  end
end

You don't need a mutex.

class Symbol
  @gensym_count = 0
  def self.gensym
    ("__gensym__%X" % @gensym_count += 1).intern
  end
end

Do you not need a Mutex _now_, since @gensym_count += 1 is effectively atomic in current ruby, or will you _never_ need a mutex?

What if?

thread 1:
   @gensym_count += 1
thread 2:
   @gensym_count += 1

thread 1:
   interpolate and intern
thread 2
   interpolate and intern

I guess it's ok since a = expr is always supposed to return expr not a, but it leaves a bad taste in my mouth.

···

On Jul 14, 2006, at 1:26 PM, Eric Hodel wrote:

On Jul 14, 2006, at 10:02 AM, Logan Capaldo wrote:

--
Eric Hodel - drbrain@segment7.net - http://blog.segment7.net
This implementation is HODEL-HASH-9600 compliant

http://trackmap.robotcoop.com

Peña wrote:

fr hal:
# Actually it was only last week that I first saw this term
# being used in the Ruby community. I wonder if it's too
# late to squash it?

you took a vacation :wink:
http://groups.google.com/group/comp.lang.ruby/browse_thread/thread/9699d659bd806203

No, I saw that thread. But it was used in a Python
context there.

Now people are applying it to Ruby. That bothers me
a little.

Hal

Eric Hodel <drbrain@segment7.net> writes:

You don't need a mutex.

class Symbol
  @gensym_count = 0
  def self.gensym
    ("__gensym__%X" % @gensym_count += 1).intern
  end
end

Okay, but now if two different files include this exact code, you're
re-generating the same symbols. This is why I used ||=, making your
code:

class Symbol
  @gensym_count ||= 0
  def Symbol.gensym
    ("__gensym__%X" % @gensym_count += 1).intern
  end
end

At the very least, two people using the same exact code to define
gensym shouldn't collide with each other.

Aparenhtly the term originated as Guerilla patch. As in Guerilla warfare :wink: And it was misunderstood and ended up being called monkey patch. Interesting read here: Monkey patch - Wikipedia

-Ezra

···

On Jul 18, 2006, at 10:36 AM, James Britt wrote:

Hal Fulton wrote:

dblack@wobblini.net wrote:

I don't know whether it qualifies as "monkeypatching" (I always
thought that meant doing something sloppy and ill-advised, which I
hope I'm not :slight_smile: but see if this helps:

Actually it was only last week that I first saw this term
being used in the Ruby community. I wonder if it's too
late to squash it?

Let's hope not.

I believe it comes from the Python community. Python (I think) allows for something similar to Ruby's ability to modify any and all classes, but my (limited) understanding is that there are enough differences between the two that what is reasonable and proper in Ruby is not quite so elegant or appropriate in Python. The term may be correct for Python, but in Ruby this is just a normal and correct way to use the language.

The use of the term indicates unease with, and lack of understanding of, Ruby on the part of the speaker, rather than describing anything about Ruby.

--
James Britt

James Britt wrote:

Hal Fulton wrote:
>
>>
>> I don't know whether it qualifies as "monkeypatching" (I always
>> thought that meant doing something sloppy and ill-advised, which I
>> hope I'm not :slight_smile: but see if this helps:
>
>
> Actually it was only last week that I first saw this term
> being used in the Ruby community. I wonder if it's too
> late to squash it?

Let's hope not.

I believe it comes from the Python community. Python (I think) allows
for something similar to Ruby's ability to modify any and all classes,
but my (limited) understanding is that there are enough differences
between the two that what is reasonable and proper in Ruby is not quite
so elegant or appropriate in Python. The term may be correct for
Python, but in Ruby this is just a normal and correct way to use the
language.

The use of the term indicates unease with, and lack of understanding of,
Ruby on the part of the speaker, rather than describing anything about Ruby.

I don't think it's quite that bad and tend to agree with _why. If
anything perhaps the original tokenizers used it to express an
uneasiness with actually doing it, but it the right hands and using the
right language (Ruby) "monkeypatching" can also be be good thing. In
other words, it's really just slang for something much more techincal
--essentially, AOP and Metaprogramming. Simliar to duck typing which is
more or less slang for Type Polymorphism.

T.

···

> dblack@wobblini.net wrote:

James Britt wrote:

Hal Fulton wrote:

I don't know whether it qualifies as "monkeypatching" (I always
thought that meant doing something sloppy and ill-advised, which I
hope I'm not :slight_smile: but see if this helps:

Actually it was only last week that I first saw this term
being used in the Ruby community. I wonder if it's too
late to squash it?

Let's hope not.

I believe it comes from the Python community. Python (I think) allows for something similar to Ruby's ability to modify any and all classes, but my (limited) understanding is that there are enough differences between the two that what is reasonable and proper in Ruby is not quite so elegant or appropriate in Python. The term may be correct for Python, but in Ruby this is just a normal and correct way to use the language.

The use of the term indicates unease with, and lack of understanding of, Ruby on the part of the speaker, rather than describing anything about Ruby.

Not necessarily. For me, the term "monkeypatch" establishes a distinction between a patch that adds functionality in a totally benign way, and a patch that changes the behavior of existing functionality using (meta)programming techniques. Because a monkeypatch, if not carefully considered, can put the main codebase into a state that causes other monkeypatches to be buggy (or worse), it is useful to be able to put a finger on the technique and teach the best practices that go along with it. Perhaps the term "monkeypatch" has bad connotations for some, but it seems to be undergoing a reclamation (for instance, the wikipedia entry does not have a negative connotation). For Pythonistas, monkeypatching may be ill advised; for Rubyists it is normal, but can cause such side effects as to be quite dangerous. By naming it, we can better control it.

Tom

···

dblack@wobblini.net wrote:

--
Tom Werner
Helmets to Hardhats
Software Developer
tom@helmetstohardhats.org
www.helmetstohardhats.org

module X
   def does_something(*options)
     p options
   end
end

module Y
   def does_something(*options)
     super('x', *options)
   end
end

class C
   include X
   include Y
end

C.new.does_something 'foo'

···

On Jul 14, 2006, at 10:24 AM, Tom Werner wrote:

Eric Hodel wrote:

On Jul 13, 2006, at 5:04 PM, Tom Werner wrote:

Eric Hodel wrote:

You just described subclasses:

Well, the idea is that the 3rd party software is going to be using whatever classes it has always used, so just creating a subclass doesn't get me anywhere because the 3rd party app (i.e. Rails) doesn't give a hoot about my BigNozzleFiretruck.

Can you give a concrete example where this takes place?

My specific purpose is to write a plugin for Rails that adds an option to each form field helper (the ones that create <input> tags) that specifies an appropriate class (since CSS attribute selector support is lacking in IE). So I'm using code like this:

module ActionView
module Helpers
   module FormHelper

     # text
     alias_method :__classless_text_field, :text_field
     def text_field(object_name, method, options = {})
       __classless_text_field(object_name, method, options.merge({:class => (options[:class].to_s + ' text').strip}))
     end

   end
end
end

--
Eric Hodel - drbrain@segment7.net - http://blog.segment7.net
This implementation is HODEL-HASH-9600 compliant

http://trackmap.robotcoop.com

Daniel Martin wrote:

Eric Hodel <drbrain@segment7.net> writes:

> You don't need a mutex.
>
> class Symbol
> @gensym_count = 0
> def self.gensym
> ("__gensym__%X" % @gensym_count += 1).intern
> end
> end

Okay, but now if two different files include this exact code, you're
re-generating the same symbols. This is why I used ||=, making your
code:

class Symbol
  @gensym_count ||= 0
  def Symbol.gensym
    ("__gensym__%X" % @gensym_count += 1).intern
  end
end

What if the symbol is already used?

  define_method( :__gensym__0 ) do
    ...
  end

  Symbol.gensym => :__gensym__0

I know, unlikley, but gensym's return is not neccessarily unique.

T.

if two different files have this exact same code inlined the programmer should
be shot. if two different files require a third that contains this code then
there will be no problem.

2 cts.

-a

···

On Sat, 15 Jul 2006, Daniel Martin wrote:

Eric Hodel <drbrain@segment7.net> writes:

You don't need a mutex.

class Symbol
  @gensym_count = 0
  def self.gensym
    ("__gensym__%X" % @gensym_count += 1).intern
  end
end

Okay, but now if two different files include this exact code, you're
re-generating the same symbols. This is why I used ||=, making your
code:

class Symbol
@gensym_count ||= 0
def Symbol.gensym
   ("__gensym__%X" % @gensym_count += 1).intern
end
end

At the very least, two people using the same exact code to define
gensym shouldn't collide with each other.

--
suffering increases your inner strength. also, the wishing for suffering
makes the suffering disappear.
- h.h. the 14th dali lama

The term "hacking" used to have a negative slant on it, too. It's not the term,
though, it's the slant that matters. The term can't be taken back, you know?
So what exactly does it mean?

Well, because it's derogatory in origin, it's also ambiguous. It's got a nested
image of primates performing informal needlework. Conversely, there's an image
of crafty and agile treeswingers going up into the lofts, the places where the
rigid, upright (sticky and pale) homosapien fails.

Plus, I like the irony of Rubyists monkeypatching the word monkeypatching:
gutting it, redefining it, and dangerously executing it in public.

_why

···

On Sat, Jul 15, 2006 at 08:41:17AM +0900, Hal Fulton wrote:

Pe?a wrote:
>fr hal:
># Actually it was only last week that I first saw this term
># being used in the Ruby community. I wonder if it's too
># late to squash it?
>
>you took a vacation :wink:
>http://groups.google.com/group/comp.lang.ruby/browse_thread/thread/9699d659bd806203
>

Now people are applying it to Ruby. That bothers me
a little.

Hi --

···

On Wed, 19 Jul 2006, Tom Werner wrote:

James Britt wrote:

Hal Fulton wrote:

dblack@wobblini.net wrote:

I don't know whether it qualifies as "monkeypatching" (I always
thought that meant doing something sloppy and ill-advised, which I
hope I'm not :slight_smile: but see if this helps:

Actually it was only last week that I first saw this term
being used in the Ruby community. I wonder if it's too
late to squash it?

Let's hope not.

I believe it comes from the Python community. Python (I think) allows for something similar to Ruby's ability to modify any and all classes, but my (limited) understanding is that there are enough differences between the two that what is reasonable and proper in Ruby is not quite so elegant or appropriate in Python. The term may be correct for Python, but in Ruby this is just a normal and correct way to use the language.

The use of the term indicates unease with, and lack of understanding of, Ruby on the part of the speaker, rather than describing anything about Ruby.

Not necessarily. For me, the term "monkeypatch" establishes a distinction between a patch that adds functionality in a totally benign way, and a patch that changes the behavior of existing functionality using (meta)programming techniques. Because a monkeypatch, if not carefully considered, can put the main codebase into a state that causes other monkeypatches to be buggy (or worse), it is useful to be able to put a finger on the technique and teach the best practices that go along with it. Perhaps the term "monkeypatch" has bad connotations for some, but it seems to be undergoing a reclamation (for instance, the wikipedia entry does not have a negative connotation). For Pythonistas, monkeypatching may be ill advised; for Rubyists it is normal, but can cause such side effects as to be quite dangerous. By naming it, we can better control it.

How about we call the whole thing "programming", and at the same time
do whatever we can to bring it about that people do this "programming"
thing as intelligently and safely as possible? :slight_smile:

David

--
http://www.rubypowerandlight.com => Ruby/Rails training & consultancy
Ruby for Rails => RUBY FOR RAILS (reviewed on
                                     Slashdot, 7/12/2006!)
http://dablog.rubypal.com => D[avid ]A[. ]B[lack's][ Web]log
dblack@wobblini.net => me