William Song
William Song

Reputation: 575

Make methods dynamic in ruby

Define a class as follows. I want to call one_method dynamically. By default, wow.one_method calls the first one_method. If I want to change the behavior of the method, just call redefine.

I can implement the method as a function type property, but that is not what I want.

If I use the following code directly, it would report errors. Could you modify it slightly.

class Wow

    def one_method
        puts "hello Ruby"
    end

    def redefine(what="none")
        define_method :one_method do 
        puts what
        end
    end
end

wow = Wow.new
wow.redefine("Whatever I want.")

Upvotes: 0

Views: 66

Answers (2)

fl00r
fl00r

Reputation: 83680

I would recommend achieving your goal in a more canonical way, just redefine the method on the instance itself:

class Wow
  def one
    :one
  end
end

w = Wow.new
w.one
#=> :one

def w.one
  :two
end

w.one
#=> :two

Drawbacks are:

  • your methods lookup table caches will be dropped
  • the code is becoming more obscure and hard to debug

Alternatives:

I don't know your real problem, but for your particular question it is better to parameterize your one_method method just to receive an argument for puts. Also, you can pass a block, so you will receive more grained control over the behavior.

Upvotes: 0

Surya
Surya

Reputation: 16012

You can achieve that via class_eval or instance_eval:

class Wow

  def one_method
    puts "hello Ruby"
  end

  def redefine(what="none")
    self.class.class_eval do
      define_method :one_method do 
        puts what
      end
    end
  end
end

wow = Wow.new
wow.one_method #=> hello Ruby

wow.redefine("Whatever I want.")
wow.one_method #=> Whatever I want.

Reason is that define_method defines instance method on the receiver and is a class's instance method so you'll have to call it on the eigen class of the object that you want to redefine a method on.

Upvotes: 1

Related Questions