Last Updated: February 25, 2016
·
2.834K
· 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.