SafeMonkeypatch

Gem Version Build Status

If you can, please, DO NOT monkeypatch.

Sometimes I patch ActiveRecord, but ain't sure if it's gonna break in the next version of Rails. This gem would raise an error if monkey patched method has changed since last time you've adapted your patch to the upstream.

Simple usage

Assume some upstream in foo.gem:

class Foo
  def bar
    puts "do first thing"
    puts "do second thing"
    puts "do something else"
  end
end

And your code:

class Foo
  safe_monkeypatch :bar, md5: "", error: "patch for foo.gem v0.0.1"

  def bar
    puts "do first thing"
    # puts "do second thing" # don't do that
    puts "do something else"
  end
end

NOTE: do this BEFORE monkeypatch happens

You can also use any of Digest::*** checksum's methods: sha1 or even combine md5+sha1

Until upstream code isn't changed, it's working like usual. But if the new version changes the implementation of Foo#bar method, an error is raised while startup time (unless you monkeypatch in runtime, but now you're on your own):

SafeMonkeypatch::UpstreamChanged: Foo#bar expected to have md5 checksum: "", but has: ""
patch for foo.gem v0.0.1

Matching usage

Unfortunately, if you have to support different versions of foo.gem, you have to monkeypatch all the variant implementation. Use blocks for this:

class Foo
  safe_monkeypatch :bar, md5: 'some_checksum' do
    def bar
      "this works if upstream Foo#bar method has md5 of source equals 'some_checksum'
    end
  end

  safe_monkeypatch :bar, md5: 'another_checksum' do
    def bar
      "this works if upstream Foo#bar method has md5 of source equals 'another_checksum'
    end
  end
end

Note: if source of upstream change dramatically and you haven't such matching checksum, you won't notice this, as such use this method:

class Foo

  # NOTE: insert this BEFORE ANY monkeypatch: list all checksum variants
  safe_monkeypatch :bar, md5: ['some_checksum', 'another_checksum']

  safe_monkeypatch :bar, md5: 'some_checksum' do
    def bar
      "this works if upstream Foo#bar method has md5 of source equals 'some_checksum'
    end
  end

  safe_monkeypatch :bar, md5: 'another_checksum' do
    def bar
      "another patch"
      "this works if upstream Foo#bar method has md5 of source equals 'another_checksum'
    end
  end
end

Use block matching always this way. This will guarantee, that any of patches will be applied, or you'll get standard exception.

Advanced usage

You can also use it without patched module scope (for proper error use patched class/module name):

Foo.safe_monkeypatch Foo.instance_method(:bar), md5: 'invalid_checksum'

Happy monkeypatching :)