Last Updated: February 25, 2016
·
2.911K
· jamesmartin

Temporarily Suppress or Replace a Method on a Ruby Object

You probably shouldn't do this for real, but it's a neat trick that I seriously considered using to get around validations on an associated ActiveRecord model.

class Suppress
 def initialize method
 @method_name = method.to_sym
 end

 def self.method_named method
 new method
 end

 def on_object object, &block
 @object ||= object
 @original_method ||= @object.method(@method_name).unbind
 if block_given?
 method = block
 else
 method = Proc.new { puts "suppressed" }
 end
 @object.define_singleton_method(@method_name) do
 method.call
 end
 self
 end

 def empower
 method = @original_method.bind(@object)
 @object.define_singleton_method(@method_name) do
 method.call
 end
 end
end

if $0 == __FILE__
 require 'ostruct'

 a = OpenStruct.new(:our_method => "The default return value")
 puts a.our_method

 x = Suppress.method_named(:our_method).on_object(a)
 puts a.our_method

 x.on_object(a) { puts "our own return value" }
 puts a.our_method

 x.empower
 puts a.our_method
end


# Output...
# The default return value
#
# suppressed
#
# our own return value
#

Notice that we can use the Suppress object to replace, or suppress, any method on the target object.

The magic is in the use of the Method#unbind method, which literally gives us a proc that's been disassociated from the original object, allowing us to save it for later and then bind the original object to a new method instance, using the old method as a receiver (yes, that's a little confusing I know). Then we simply define_singleton_method on the original object, calling the newly bound instance of the original method.