Skip to content

Instantly share code, notes, and snippets.

@rkh
Forked from lmarburger/hack.rb
Created May 17, 2012 16:31
Show Gist options
  • Save rkh/2720016 to your computer and use it in GitHub Desktop.
Save rkh/2720016 to your computer and use it in GitHub Desktop.
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
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
Copy link
Author

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