Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
require 'test/unit/assertions'
include Test::Unit::Assertions
assert_equal "1.9.2", RUBY_VERSION
# A Ruby meta-programming puzzle for polite programmers.
# This puzzle was created by Matt Wynne (@mattwynne) on 2011-04-10 inspired by Jim Weirich's
# talk at the Scottish Ruby Conference 2011.
#
# The challenge is: you have a class Foo which you want to monkey-patch, but politely.
# Here's the default behaviour of Foo
class Foo
def bar
"bar"
end
end
assert_equal "bar", Foo.new.bar
module Baz
def bar
"wee" + super
end
end
# # Unfortunately, if we try to monkey-patch Foo like this, it won't work
# class Foo
# include Baz
# end
#
# # this fails:
# assert_equal "weebar", Foo.new.bar
#
# This is because the include trick inserts the module Baz higher into the inheritance tree
# meaning Foo's own implementation of bar is hit first.
# So let's write a helper method that can do the patching for us, by extending each instance
# of Foo as it is created.
#
def extend_every(type_to_patch, module_to_apply)
patcher = Module.new do
# Use define method to get closure-type behaviour within the method "body"
define_method(:new) do |*args|
super(*args).extend(module_to_apply)
end
end
type_to_patch.class_eval do
extend patcher
end
end
extend_every(Foo, Baz)
assert_equal "weebar", Foo.new.bar # => works.
@mattwynne

This comment has been minimized.

Copy link

commented Apr 10, 2011

I did not know you could do *args to a block like that. Ruby 1.9 FTW!

@myronmarston

This comment has been minimized.

Copy link

commented Apr 11, 2011

I think you'd want to do:

define_method(:new) do |*args, &block|
  super(*args, &block).extend(module_to_apply)
end

It doesn't matter in this case (since Foo.new does not expect a block), but if it did, this would be important.

@lazyatom

This comment has been minimized.

Copy link
Owner Author

commented Apr 11, 2011

Yep, that's a good point.

@samnang

This comment has been minimized.

Copy link

commented Jul 17, 2011

Here's my revision: https://gist.github.com/1087802

I make it into shorter by using define_singleton_method

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.