[ANN] monkeypatch-0.1.2 (alpha)

monkeypatch is a new ruby projects that tries to protect you from
patch
collisions.

== Project info

monkeypatch introduces a small API that you can use when you want to
fix
external libraries. The mechanism is meant to be as straightforward as
possible. Also, by adding monkeypatch as a gem dependency, you declare
that
your package is fixing other package’s code.

Version: 0.1.2
Stability: unstable
Source code: GitHub - zimbatm/monkeypatch: Safe(er) monkey patching in Ruby
RDoc: http://rdoc.info/projects/zimbatm/monkeypatch
Blog: http://oree.ch

== A small example


my_patch = MonkeyPatch.add_method(:to_blob) do
def to_blog; “” end
end

my_patch.patch_class(String)

“hello”.to_blob #=> “”

== How does it work ?

When applying a patch, there are two mechanism that enters the game ;
conditions and conflicts.

  • Conditions are not fatal. If they don’t match, the patch is simply not
    applied, and a message is passed to the MonkeyPatch.logger.
    Conditions are for library version matching for example. They are
    extensible buy using: #add_condition(msg, &cond)
  • Conflicts are fatal, in that they raise a ConflictError if they don’t
    match. Conflicts are for patch collision. They are not extensible and
    provided by
    the API. The idea is that if your code has conflicting patches, you
    will
    get notified by a crashing application, so that you can fix it as
    early as
    possible.

Another aspect are PatchSet (s). If you have a bundle of patches you
want to
apply, you can aggregate them with the & operator. You then get a
PatchSet
instance which you can use like a Patch. This is not fully done now, but
theoritically, your bundle does not apply unless all patch conditions
are met.

== What’s next ?

This project API is intentionally unstable for now. What I’m really
looking for
now, is community feedback. I also intend to look at real-world patches
to
see how this library can be made more useful.

So if you have some real-world patches or any comments, please give me
your
feedback !

Cheers,
zimbatm

On Tue, 26 May 2009 19:10:25 +0900
“zimbatm …” [email protected] wrote:

your
feedback !

Cheers,
zimbatm

Hey zimbatm,

I’ll dig into this more later, but this seems like a really nice piece
of work,
simple, yet powerful.
Now we just gotta convince everybody to use it :slight_smile:

Michael F. wrote:

Hey zimbatm,

I’ll dig into this more later, but this seems like a really nice piece
of work,
simple, yet powerful.
Now we just gotta convince everybody to use it :slight_smile:

Hi Michael,

long time no read! Yes, you spotted the weak point right-on, getting
adoption is critical. This is why I am trying to keep it as simple as
possible. Also, I believe nobody really wants to learn a new API in this
already overloaded technology world. This is why my next step is to
start converting real-world patches to show the was.

Can’t wait to read your feedback.

Cheers,
zimbatm

trans wrote:

Besides the issues you mention (new API and adoption), I see other
reasons something like this isn’t going to make much headway.

  1. You can use -w ruby option to get warnings about methods that are
    redefined.

  2. Raising an error on a method redefinition is too much. There are
    times when that’s what you need to do, say to temporary patch a bug.

Really? Raising an error is too much? Consider that if you get this
warning, what it really means is that you just clobbered someone else’s
method and their code will most definitely fail because of this. If this
isn’t the time for “fail fast” then I don’t know what is. Clobbering a
method (for example to patch a bug) should be the exception case; then
you could easily undef the method before defining your patched version.

Daniel

On May 26, 4:35 pm, “zimbatm …” [email protected] wrote:

long time no read! Yes, you spotted the weak point right-on, getting
adoption is critical. This is why I am trying to keep it as simple as
possible. Also, I believe nobody really wants to learn a new API in this
already overloaded technology world. This is why my next step is to
start converting real-world patches to show the was.

Besides the issues you mention (new API and adoption), I see other
reasons something like this isn’t going to make much headway.

  1. You can use -w ruby option to get warnings about methods that are
    redefined.

  2. Raising an error on a method redefinition is too much. There are
    times when that’s what you need to do, say to temporary patch a bug.

  3. Using a standardized extension library like ActiveSupport or Facets
    is a fairly safe bet for the more common extensions.

  4. Using a unique method name, eg. String#smith_to_s if your project
    is called Smith, is a fairly safe bet for more specialized extensions.

Given all this I don’t think this kind of API is worth the effort. The
real solution is, of course, Selector Namespaces. That’s a more worthy
pursuit, IMO. But as far as I know, no one has yet been able to come
up with a fully comprehensible and user-friendly design for that. I
start to wonder if it is even possible.

–Tom

On Fri, May 29, 2009 at 5:59 PM, Daniel DeLorme [email protected]
wrote:

Really? Raising an error is too much? Consider that if you get this warning,
what it really means is that you just clobbered someone else’s method and
their code will most definitely fail because of this. If this isn’t the time
for “fail fast” then I don’t know what is. Clobbering a method (for example
to patch a bug) should be the exception case; then you could easily undef
the method before defining your patched version.
And actually that should be the only way to redefine a method, I was
really greatful when someone pointed out the warnings I produced in my
code.
In Runy1.9 it can be a little bit teadious to remove the method before
redefining it, or is there a better way than
remove_method ?
I would love to have a
define_method!
method which does all the internals for me.
Just my microbucks.
Cheers
Robert

Daniel


Toutes les grandes personnes ont d’abord été des enfants, mais peu
d’entre elles s’en souviennent.

All adults have been children first, but not many remember.

[Antoine de Saint-Exupéry]

Hi ruby fellows, I’m back !

The discussion was mainly oriented on the stricktness of the patch
application. Should it
raise an error ? Should it just output a warning ? Here is my point of
view:

I’d like to differentiate two things. Changing code inside and outside
from your project.
What monkeypatch tries to do, it to give some protection when you patch
external code, because you have less control on it (it might be upgrade
to the latest version and you don’t know). When you’re changing your own
code, then it’s your responsibility to do it right.

This is why I believe that patches should be strict and must apply to
three things:

  • Be coherent (not adding a method and then replace it in the same
    patch for example). In that case, it should give you an early warning by
    raising and exception.
  • Only apply if some conditions, like if the bug still exists, are
    met. If not, don’t apply the patch.
  • Finally, if the patches applies, it should not do unexpected things,
    like renaming a method that doesn’t exist. This is the second case where
    it should raise.

Cheers,
zimbatm

On May 29, 11:59 am, Daniel DeLorme [email protected] wrote:

Really? Raising an error is too much? Consider that if you get this
warning, what it really means is that you just clobbered someone else’s
method and their code will most definitely fail because of this. If this
isn’t the time for “fail fast” then I don’t know what is. Clobbering a
method (for example to patch a bug) should be the exception case; then
you could easily undef the method before defining your patched version.

I don’t think there’s anything wrong with your assessment. That
certainly is an option. It would be interesting to know Matz’ reason
for not raising an error, and instead just issuing a warning when a
method is redefined.

Perhaps the reason is subtle. If I load extension lib A before
extension lib B, I am giving B priority over A in so far as they
conflict. If errors were raised instead, as you suggest, I would
simply not be able to use the two together without first undefining
all the methods of lib A for which they may conflict. That could be
very annoying, especially if there a number of conflicts and I was
only interested in using a few methods from each lib to begin with.

-Tom

Thomas S. wrote:

Besides the issues you mention (new API and adoption), I see other
reasons something like this isn’t going to make much headway.

The next version will support a better conflict detection engine that
should make it robust
enough to be useful even with zero adoption.

  1. You can use -w ruby option to get warnings about methods that are
    redefined.

This is not the same. It will also catch method redefinitions inside
your project. Monkeypatch is for external code (core, stdlib and vendor)
patching.

  1. Raising an error on a method redefinition is too much. There are
    times when that’s what you need to do, say to temporary patch a bug.

Discussed earlier. But to be precide, the idea is to add a non-raising
condition that will detect
if the bug still exists before applying the patch.

  1. Using a standardized extension library like ActiveSupport or Facets
    is a fairly safe bet for the more common extensions.

I agree by some degree. But it’s also what makes them so popular.
Because testing your code against different extension libraries is
exponentially difficult.

  1. Using a unique method name, eg. String#smith_to_s if your project
    is called Smith, is a fairly safe bet for more specialized extensions.

true but it doesn’t apply for all the cases.

Given all this I don’t think this kind of API is worth the effort. The
real solution is, of course, Selector Namespaces. That’s a more worthy
pursuit, IMO. But as far as I know, no one has yet been able to come
up with a fully comprehensible and user-friendly design for that. I
start to wonder if it is even possible.

One doesn’t forbid the other. Selector namespaces is an interesting
idea, and you’ll see
I point to a similar project in the README.rdoc file.

What I want, is a tool I can use right now, which doesn’t require to
change the language.
Maybe this project can also be a tool to promote alternative and better
solutions in the longer term. What do you think ? Send me your critical
comments, I like it :slight_smile:

Cheers,
zimbatm

:: News from the front ::

One aspect where I agree with Thomas S. is that the API should be
minimalized. Even I, don’t like to learn new things if they aren’t funny
(and this project is useful but not really exciting).

By skimming the existing patches on github, I saw, oh horror, that I
completely ignored the common extension case:

+++++++++++++++++
module MyStringExtension
def to_moo
“moo!”
end
end

class String
include MyStringExtension
end

or String.send(:include, MyStringExtension)


This is why the next version (not even on github yet) will use a
dramatically different API. Not everything is fixed yet, but it should
look like this:

++++++++++++++++++
module MyStringExtension
extend MonkeyPatch
def to_moo
“moo!”
end
end

class String
include MyStringExtension
end

Pretty close isn’t it ? This is the simple case where you just want to
add methods to an external class. If you start to redefine, alias or
remove methods, it’s a bit more complicated. But just like this, you get
the same as before, with the added assurance that is will not override
another #to_moo method.

Cheers,
zimbatm

On Jun 9, 5:09 pm, “zimbatm …” [email protected] wrote:

What I want, is a tool I can use right now, which doesn’t require to
change the language.
Maybe this project can also be a tool to promote alternative and better
solutions in the longer term. What do you think ? Send me your critical
comments, I like it :slight_smile:

Perhaps so.

I’m not sure it is worth anything at all but I did have a notion for
this kind of thing the other day. Let’s say I want to “monkey patch”
String for my application called FooApp.

class String
def to_blog
“”
end
end

Would it behoove us if I laid a claim to it?

module FooApp
claim String, :to_blog
end

Then if someone came along and tried to redefine it, the system could
say “Hey you! FooApp has claimed String#to_blog.”

Seems like that approach would offer a “lighter-touch” alternative,
rather then having to use a different module system, ie. the
MonkeyPatch class.

Not sure about implementation though.

T.