Method equality; setting instance variables on Method instan


#1

A few questions about the code below:

  1. Why do m1 and m2 have different IDs but compare as equal?
  2. Why does m2 invoke the same old functionality when run?
  3. Why did my instance_variable_set not ‘take’?

I suspect all answers have something to do with where the methods are
being defined/shadowed, but I’ll be damned if I can figure them out.

slim:~/Desktop/test gavinkistner$ ls -Flag
total 8
drwxr-xr-x 3 staff 102 Dec 24 10:15 ./
drwx------ 34 staff 1156 Dec 24 10:15 …/
-rw-r–r-- 1 staff 734 Dec 24 10:20 override.rb

slim:~/Desktop/test gavinkistner$ cat override.rb
class Module
def override( method_name, &block )
orig_method = method( method_name )
new_name = “m#{rand(1000000000)}”
alias_method new_name, method_name
define_method( method_name, &block )
method( method_name ).instance_variable_set( :@__orig_method_name,
new_name )
end

def restore( method_name )
alias_method method_name, method( method_name
).instance_variable_get( :@__orig_method_name )
end
end

m1 = Kernel.method( :system )
system ‘ls’

Kernel.override( :system ){ |code| puts "#{code} not allowed"}
m2 = Kernel.method( :system )
system ‘ls’

p m1, m2, m1==m2, m1.object_id, m2.object_id
m1.call( ‘ls’ )
m2.call( ‘ls’ )

Kernel.restore( :system )
m3 = Kernel.method( :system )
system ‘ls’

END

slim:~/Desktop/test gavinkistner$ ruby override.rb
override.rb
ls not allowed
#<Method: Kernel.system>
#<Method: Kernel.system>
true
953400
953250
override.rb
override.rb
override.rb:11:in alias_method': nil is not a symbol (TypeError) from override.rb:11:inrestore’
from override.rb:26


#2

Phrogz wrote:

A few questions about the code below:

  1. Why do m1 and m2 have different IDs but compare as equal?
  2. Why does m2 invoke the same old functionality when run?
  3. Why did my instance_variable_set not ‘take’?

Another data point on that last question: at least it looks like you
can set/get instance variables on a Method instance in general:

irb(main):001:0> m1 = Kernel.method( :system )
=> #<Method: Kernel.system>
irb(main):002:0> m1.instance_variable_set( :@foo, :bar )
=> :bar
irb(main):003:0> m1.instance_variables
=> ["@foo"]
irb(main):004:0> m1.instance_variable_get( :@foo )
=> :bar

In case it wasn’t obvious, what I am trying to achieve with the above
code is a stack-like method override/restore system. Each time you
override a method, the method should store the name of the
randomly-chosen alias for the old method.

A workaround will be to store the stack of method names on the module
itself (which I’ll try next); nonetheless, I’m interested in why my
original code didn’t work as I expected.