Home > Software engineering >  Is there a way to know if a ruby method has been redefined?
Is there a way to know if a ruby method has been redefined?

Time:10-24

I want to know if a method on a class gets redefined.

use case: in version 1 of ruby-clock, defining a method on an object was part of the API. In version 2, doing so will break behavior, and a different API should be used.

what I ended up doing: https://github.com/jjb/ruby-clock/pull/28/files


The best would be if I could use some sort of metaprogramming to notice when it happens.:

# this is example code which does not work
class MyClass
  def my_method
    puts "hello"
  end

  # this is not real! it's an example of what i hope is somehow possible
  def self.method_defined(m, *args, &block)
    if :my_method == m
      raise "you should not redefine my_method!"
    end
  end
end

Also acceptable would be if I could check back later and see if it has changed.

# this is example code which does not work
class MyClass
  def my_method
    puts "hello"
  end

  @@my_method_object_id = get_method_object_id(:my_method)
end

# later, when app initialization is done, check if the user redefined the method
# this is not real! it's an example of what i hope is somehow possible
def self.method_defined(m, *args, &block)
  if MyClass.get_method_object_id(:my_method) != MyClass.my_method_object_id
    raise "you should not redefine my_method!"
  end
end

n.b. that MyClass.method(:my_method) is a real thing in ruby, but it always produces a new object.

CodePudding user response:

This sounds pretty much like XY problem. I don't think you should go this way - if you find yourself fighting with the Ruby object model, you most probably picked the wrong tool for the job.

But for the sake of completeness... There are a couple of callbacks in Module that can help with the ideas as crazy as this one. In particular, there is Module#method_added which is called each time some instance method is defined. So we can do smth. like this (very dirty example):

class C
  @foo_counter = 0

  def self.method_added(name) 
    if name == :foo
      @foo_counter  = 1
      if @foo_counter > 1
        raise "Don't redefine foo!"
      end
    end  
  end  
  
  def foo
    "foo"
  end  
end  

now if one tries to open C and redefine foo the following happens (pry session as an example):

pry(main)> class C
pry(main)*   def foo
pry(main)*     "bar"
pry(main)*   end  
pry(main)* end

RuntimeError: Don't redefine foo!

So you can do what you want to (to some extent - it's Ruby, so what stops one from redefining the annoying hook first? :)), but please don't. You'll get nothing but troubles...

CodePudding user response:

You could use the callback BasicObject#singleton_method_added.

class C
  @class_methods = []

  def self.singleton_method_added(m)
    puts "Method #{m} was just redefined" if @class_methods.include?(m)
    @class_methods << m
  end

  def self.c
    puts 'hi'
  end
end
C.c
  #=> "hi"
class C
  def self.c
    puts 'ho'
  end
end
Method c was just redefined
C.c #=> "ho"
    
  • Related