Overriding Class Methods With Modules

How do I override a class method on an object with another module? For example:

   class TestObject
     def self.my_method
       "override me"
     end
   end

   module TestExtension
     def my_method
       "overridden by TestExtension"
     end
     def another_method
       "another method"
     end
   end

   TestObject.extend TestExtension

   puts TestObject.my_method #=> "override me"
   puts TestObject.another_method #=> "another method"

Why can't I get TestObject to use my_method from TestExtension?

···

--
John Long
http://wiseheartdesign.com
http://radiantcms.org

John W. Long wrote:

How do I override a class method on an object with another module? For
example:

   class TestObject
     def self.my_method
       "override me"
     end
   end

   module TestExtension
     def my_method
       "overridden by TestExtension"
     end
     def another_method
       "another method"
     end
   end

   TestObject.extend TestExtension

   puts TestObject.my_method #=> "override me"
   puts TestObject.another_method #=> "another method"

Why can't I get TestObject to use my_method from TestExtension?

Because we don't have Cuts :wink:

Okay a more presently practical answer. You can use a hack like:

    module TestExtension
      def self.included(base)
        base.module_eval do
          def my_method
            "overridden by TestExtension"
          end
        end
      end
      def another_method
        "another method"
      end
    end

T.

Hi --

How do I override a class method on an object with another module? For example:

class TestObject
   def self.my_method
     "override me"
   end
end

module TestExtension
   def my_method
     "overridden by TestExtension"
   end
   def another_method
     "another method"
   end
end

TestObject.extend TestExtension

puts TestObject.my_method #=> "override me"
puts TestObject.another_method #=> "another method"

Why can't I get TestObject to use my_method from TestExtension?

What you're seeing is that in the method lookup path, a given class
comes before any modules it includes. In your case, the class in
question is the singleton class of TestObject: that class defines
#my_method, and also includes a module that defines #my_method. The
one defined in the class "wins".

Here's another, parallel example:

   module M
     def my_method
       puts "M#my_method"
     end
   end

   class C
   end

   c = C.new

   class << c # defined the method in the class
     def my_method
       puts "C#my_method"
     end
   end

   c.extend(M) # also include the module in the class
   c.my_method # "C#my_method" (the class wins)

The module would win if the method were defined in the *class* of the
object, rather than the singleton class -- because then the lookup
order would be:

   singleton class (no)
   module included by singleton class (yes -- execute method)
   [class (never reached)]

David

···

On Wed, 30 Aug 2006, John W. Long wrote:

--
                   David A. Black | dblack@wobblini.net
Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB's Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] Ruby for Rails | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org

Trans wrote:

Okay a more presently practical answer. You can use a hack like:

    module TestExtension
      def self.included(base)
        base.module_eval do
          def my_method
            "overridden by TestExtension"
          end
        end
      end
      def another_method
        "another method"
      end
    end

Mmm. That doesn't work either.

···

--
John Long
http://wiseheartdesign.com
http://radiantcms.org

Trans wrote:
> Okay a more presently practical answer. You can use a hack like:
>
> module TestExtension
> def self.included(base)

shouldn't be "extended" ?

···

On 8/30/06, John W. Long <ng@johnwlong.com> wrote:

> base.module_eval do
> def my_method
> "overridden by TestExtension"
> end
> end
> end
> def another_method
> "another method"
> end
> end

Mmm. That doesn't work either.

That works. So the final code would be something like:

module TestExtension
  def inject_methods(base)
    base.module_eval do
       def self.my_method
         "overridden by TestExtension"
       end
    end
  end

  def extended(base)
    inject_methods(base)
  end

  def included(base)
    inject_methods(base)
  end
end

This has always been a little clunky in Ruby, primarily because
methods written within a specific class were designed to override
mixin and parent class methods, not vice-versa.

···

On 8/30/06, Jan Svitok <jan.svitok@gmail.com> wrote:

On 8/30/06, John W. Long <ng@johnwlong.com> wrote:
> Trans wrote:
> > Okay a more presently practical answer. You can use a hack like:
> >
> > module TestExtension
> > def self.included(base)

shouldn't be "extended" ?

> > base.module_eval do
> > def my_method
> > "overridden by TestExtension"
> > end
> > end
> > end
> > def another_method
> > "another method"
> > end
> > end
>
> Mmm. That doesn't work either.