Skip to content

Instantly share code, notes, and snippets.

@forforf
Created October 28, 2011 02:52
Show Gist options
  • Save forforf/1321516 to your computer and use it in GitHub Desktop.
Save forforf/1321516 to your computer and use it in GitHub Desktop.
Unbind, destroy and recreate a method at runtime. Meta-programming goodness
#This captures the Array#sort method and stores it in m
m = Array.instance_method(:sort)
#Shows that sort still works
[3,2,1].sort
#=> [1,2,3]
#Here we override the array sort method
#Array#sort no longer works
[3,2,1].class.send(:define_method, :sort){nil}
[3,2,1].sort
#=> nil
#Here's where we attach the original sort back
#to an array object
new_sort = nil
[3,2,1].tap{|me| new_sort = m.bind(me).call}
#=> [1,2,3]
#Breaking it down
# Because tap returns itself, we need to
# declare a variable to capture the output
# of the function modifying itself
# new_sort = nil
# Use tap to get a reference to the object
# [3,2,1].tap{|me| ... me is [3,2,1] here ...}
# Bind the original sort method to the current object
# m.bind(me)
# call the bound original sort method and assign
# the result to the variable we initialized
# new_sort = m.bind(me).call
# Ed. note: Verify and clean up to be more clear and concise
# this is *almost* the equivalent of calling me.sort
# the difference is that in this case the method is not
# permanently bound to the object, as the block doing the
# binding doesn't persist.
# new_sort now contains the result of applying the method
#We can put improve tap now
class Object
def tapout(block)
ret_val = nil
self.tap{|me| ret_val = block.call(me)}
ret_val
end
end
[5,3,1].respond_to? :tapout
#=> true
wrap_sort = lambda{|o| m.bind(o).call}
[5,3,1].tapout(wrap_sort)
#=> [1,3,5]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment