Instantly share code, notes, and snippets.

@rkh /hack.rb forked from lmarburger/hack.rb
Created May 17, 2012

Embed
What would you like to do?
Possible for a module included somewhere to override a class's instance method?
class Base
def call
'call'
end
end
p Base.new.call #=> "call"
# Monkeypatching "works" but doesn't provide access to #super
class Base
def call
'monkeypatched'
end
end
module Prepending
def append_features(base)
return super unless base.is_a? Class
prepend = self
base.extend Module.new { define_method(:new) { |*| super.extend(prepend) }}
end
end
p Base.new.call #=> "monkeypatched"
# This is the spirit of what I'd like, but methods defined on the class will be
# preferred over those in ancestors.
module Override
extend Prepending
def call
[ 'overridden', super ]
end
end
class Base
include Override
end
p Base.new.call #=> ["overridden", "monkeypatched"]
# This works but I don't have access to the class instances to apply this method.
instance = Base.new
class << instance
def call
[ 'overridden', super ]
end
end
p instance.call #=> ["overridden", ["overridden", "monkeypatched"]]
@shime

This comment has been minimized.

Copy link

shime commented May 18, 2012

What's "_" in base.extend Module.new { define_method(:new) { |_| super.extend(prepend) }}?

Shouldn't it look like this instead? base.extend Module.new { define_method(:new) { |*args| super(*args).extend(prepend) }}

@rkh

This comment has been minimized.

Copy link
Owner

rkh commented May 18, 2012

No, |*| is a shorthand syntax for I don't care about the arguments. super without args and parens will automatically send any arguments passed to the original call and a block, if there is one (missing from your code). So { |*| super } is equivalent to { |*args, &block| super(*args, &block) }.

@shime

This comment has been minimized.

Copy link

shime commented May 18, 2012

👍 thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment