clyfe
clyfe

Reputation: 23770

Override module method from another module

I want to override a method from a module A from another module B that will monkey-patch A.
http://codepad.org/LPMCuszt

module A
  def foo; puts 'A' end
end

module B
  def foo; puts 'B'; super; end
end

A.module_eval { include B } # why no override ???

class C
  include A
end

# must print 'A B', but only prints 'A' :(
C.new.foo

Upvotes: 15

Views: 21081

Answers (4)

Vasiliy Ermolovich
Vasiliy Ermolovich

Reputation: 24637

module A
  def foo
    puts 'A'
  end
end

module B
  def foo
    puts 'B'
    super
  end
end

include A # you need to include module A before you can override method

A.module_eval { include B }

class C
  include A
end

C.new.foo # => B A

Upvotes: 8

Mukesh Kumar Gupta
Mukesh Kumar Gupta

Reputation: 1647

This is also one solution to your question. I am trying to achieve with module hooks included. When you include the module A into class C. included callbacks defined in module A is called and executed. We included the module B on-fly. So our module A method foo is overridden by Module B foo to print the superclass module method just called super.

module A
  def self.included klass
    klass.send(:include, B)
  end
  def foo
    puts 'A'
  end
 end

module B
  def foo
   super
   puts 'B'
  end
end

class C
 include A
end
C.new.foo #out put A,B

Upvotes: 1

devkaoru
devkaoru

Reputation: 1162

Another way to accomplish this is to include module B when module A is included.

module A
  def foo
    puts "this should never be called!"
    "a"
  end
end

module B
  def foo
    "b"
  end
end

module A
  def self.included(base)
    base.class_eval do
      include B
    end
  end
end

class C
  include A
end

C.new.foo # "b"

Upvotes: 0

Jonathan
Jonathan

Reputation: 3263

Including a module places it above the module/class that is including it in the class hierarchy. In other words, A#foo is not super of B#foo but rather the other way round.

If you think of including a module as a way of doing multiple inheritance this makes sense, include SomeModule is a way of saying, "Treat SomeModule like it is a parent class for me".

To get the output you wanted you need to reverse the inclusion so that B includes A:

module A
  def foo; puts 'A' end
end

module B
  def foo; puts 'B'; super; end
end

B.module_eval { include A } # Reversing the inclusion

class C
  include B # not include A
end

puts C.new.foo

Edit in response to comment:

Then either include both A and B in C with B included after A:

# A and B as before without including B in A.

class C
  include A
  include B
end

or patch A in C itself and don't bother with B.

# A as before, no B.

class C
  include A

  def foo; puts 'B'; super; end
end

The only way for this to work is if the method lookup on C is C -> B -> A and there is no way to do this without including B into C.

Upvotes: 3

Related Questions