Forum: Ruby [ANN] monkeypatch-0.1.2 (alpha)

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
13120f1de7c199861733f2cfd349d860?d=identicon&s=25 Zimbatm ... (zimbatm)
on 2009-05-26 12:10
*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: http://github.com/zimbatm/monkeypatch/
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; "<blob>" end
end

my_patch.patch_class(String)

"hello".to_blob #=> "<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
86e33dee4a89a8879a26487051c216a8?d=identicon&s=25 Michael Fellinger (Guest)
on 2009-05-26 18:26
(Received via mailing list)
On Tue, 26 May 2009 19:10:25 +0900
"zimbatm ..." <zimbatm@oree.ch> 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 :)
13120f1de7c199861733f2cfd349d860?d=identicon&s=25 Zimbatm ... (zimbatm)
on 2009-05-26 22:35
Michael Fellinger 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 :)

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
45196398e9685000d195ec626d477f0e?d=identicon&s=25 Thomas Sawyer (7rans)
on 2009-05-29 17:05
(Received via mailing list)
On May 26, 4:35 pm, "zimbatm ..." <zimb...@oree.ch> 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
E0526a6bf302e77598ef142d91bdd31c?d=identicon&s=25 Daniel DeLorme (Guest)
on 2009-05-29 17:59
(Received via mailing list)
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
45196398e9685000d195ec626d477f0e?d=identicon&s=25 Thomas Sawyer (7rans)
on 2009-05-29 23:46
(Received via mailing list)
On May 29, 11:59 am, Daniel DeLorme <dan...@dan42.com> 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
703fbc991fd63e0e1db54dca9ea31b53?d=identicon&s=25 Robert Dober (Guest)
on 2009-05-31 00:28
(Received via mailing list)
On Fri, May 29, 2009 at 5:59 PM, Daniel DeLorme <dan-ml@dan42.com>
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]
13120f1de7c199861733f2cfd349d860?d=identicon&s=25 Zimbatm ... (zimbatm)
on 2009-06-09 23:07
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
13120f1de7c199861733f2cfd349d860?d=identicon&s=25 Zimbatm ... (zimbatm)
on 2009-06-09 23:09
Thomas Sawyer 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.

> 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.

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.

> 3. 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.

> 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.

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 :)

Cheers,
   zimbatm
13120f1de7c199861733f2cfd349d860?d=identicon&s=25 Zimbatm ... (zimbatm)
on 2009-06-09 23:16
:: News from the front ::

One aspect where I agree with Thomas Sawyer 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
45196398e9685000d195ec626d477f0e?d=identicon&s=25 Thomas Sawyer (7rans)
on 2009-06-09 23:34
(Received via mailing list)
On Jun 9, 5:09 pm, "zimbatm ..." <zimb...@oree.ch> 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 :)

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
      "<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.
This topic is locked and can not be replied to.