1

I would like to expand the functionality of some class using class_eval. I would like to force the class to inherit some methods from some other class.

I.e.:

SomeClass.class_eval do
  # force inheritence from some other class
end

What's the best way to achieve it?

2 Answers 2

1

If overriding existing functionality is a hard requirement here, you need to have those existing methods defined in a module that's also included.

class SomeClass
  include DefaultBehaviour
end

module DefaultBehaviour
  def run
    puts "ran default"
  end
end

module AlternateBehaviour
  def run
    puts "ran alternate"
  end
end

SomeClass.class_eval {
  include AlternateBehaviour
}

SomeClass.new.run #=> "ran alternate"

The reason for this is because of ruby's method lookup path.

It starts off as SomeClass -> Object.

When you include AlternateBehaviour, it becomes SomeClass -> AlternateBehaviour -> Object. So methods defined directly on SomeClass still take precedence.

However, if those methods are defined on DefaultBehaviour, the lookup path becomes SomeClass -> AlternateBehaviour -> DefaultBehaviour -> Object, so your alternate method takes priority. Whichever module was included most recently is the highest priority.

In the case where you do not have control of the original class, you can do instead:

module AlternateBehaviour
  def self.included(base)
    base.send(:remove_method, :run)
  end

  def run
    puts "ran alternate"
  end
end

Though at this point, one starts to wonder whether you might be better off by just doing

SomeClass.class_eval {
  def run
    "ran alternate"
  end
end
Sign up to request clarification or add additional context in comments.

4 Comments

The complication comes when the class method I'm trying to override sits in some 3rd party engine code that I cannot refactor into module... :( Otherwise your solution works...
Okay, updating solution then. It's only a bit more complex.
Why do you need to use class_eval? You can just dump the method definitions directly into the class_eval, or if you want the replacement methods to live elsewhere, you can build a module that removes the existing methods before defining replacements.
It would be most clear-looking solution in my given scenario. Btw, the updated solution works!
0

Try using include and extend, both explained here. They only work with modules; you just can't modify/add superclasses of a class in Ruby after it has already been created.

Only one problem: you can't override already existing methods in a class for the explained in the third comment to this post.

Also see this topic for more information.

2 Comments

How I would go in the situation when I already have a class and would like to override it's methods from what is defined in external module?
Modules won't actually work to override functionality specified in the class itself, since ruby backtracks up the inheritance hierarchy to find methods, and including a method just inserts that module into the hierarchy immediately above the class. The class itself is still the first place that's checked. ...though I would still agree with Jwosty that this is the correct solution to this problem. If you really need to override methods that are defined on the class already, you could solve this by defining them on their own module.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.