Module to overwrite method defined via define_method

Hi List !

I found a tricky thing: if you use define_method and then include a
module that contains a method with the same name, the method in the
module is just ignored.

Why is that so ?

How can I overwrite a method defined through ‘define_method’ ?

Example ruby code: http://bit.ly/2NjP5w

Gaspard

Hi –

On Mon, 14 Sep 2009, Gaspard B. wrote:

Example ruby code: http://bit.ly/2NjP5w
It’s a matter of the order of method lookup. In general, an object
looks for a method first in its class, and then in modules mixed into
that class.

module M
def x; puts “x in M”; end
end

class A
def x; puts “x in A”; end
include M
end

A.new.x # x in A

It’s the same if the method is defined with #define_method.

Yehuda K. has recently proposed that there be a way to insert a
module in the lookup order before the class. I’m not sure where that
proposal stands at the moment.

David

David A. Black wrote:

Hi –

On Mon, 14 Sep 2009, Gaspard B. wrote:

Example ruby code: http://bit.ly/2NjP5w
It’s a matter of the order of method lookup. In general, an object
looks for a method first in its class, and then in modules mixed into
that class.

Damned ! This means that all the dynamic methods created by rails with
“has_one” and such cannot be overwritten by including a module except if
you write:

module A
def self.included(base)
base.send(:define_method, ‘foo’) do
puts “‘foo’ from A”
end
end
end

Any other solution ?

Gaspard

On 9/13/09, Gaspard B. [email protected] wrote:

end

Any other solution ?

If you use extend instead of include, the methods in the module should
be seen first. Extend operates on instances, not classes, tho, so
you’ll have to call extend in your initialize.

Alternately, if you make a subclass and then use include in the
subclass, I believe that will also utilize the module version. Not
sure how that might interoperate with rails.

Hi –

On Mon, 14 Sep 2009, Gaspard B. wrote:

end

Any other solution ?

I would consider rewriting that as:

module A
def self.included(base)
base.class_eval do
def foo
puts “‘foo’ from A”
end
end
end
end

so as to normalize it back to the “def” form.

Keep in mind, too, that this is a bit fragile because the order
matters. If you include the module first and then do has_many, (or
attr_accessor, or any other instance-method generator), the has_many
will “win”.

David

David,

Thanks for the highlights and the advice , I will consider keeping the
‘def xxx’ syntax.

Gaspard

David A. Black wrote:

I would consider rewriting that as:

module A
def self.included(base)
base.class_eval do
def foo
puts “‘foo’ from A”
end
end
end
end

so as to normalize it back to the “def” form.

Keep in mind, too, that this is a bit fragile because the order
matters. If you include the module first and then do has_many, (or
attr_accessor, or any other instance-method generator), the has_many
will “win”.

David

Gaspard B. wrote:

Hi List !

I found a tricky thing: if you use define_method and then include a
module that contains a method with the same name, the method in the
module is just ignored.

Why is that so ?

By the way, you can get the lookup order using the ancestors method:

module Redef
def foo
puts “‘foo’ from module”
end
end

class A
define_method(“foo”) do
puts “‘foo’ from define_method”
end

include Redef
end

p A.ancestors

–output:–
[A, Redef, Object, Kernel]

Although, ancestors() doesn’t include the singleton classes:

module Redef
def foo
puts “‘foo’ from module”
end
end

class A
def foo
puts “foo from class A”
end

def initialize
class << self
include Redef
end
end

end

a = A.new
a.foo
p A.ancestors

–output:–
[A, Object, Kernel]

7stud – wrote:

class A
def foo
puts “foo from class A”
end

def initialize
class << self
include Redef
end
end

end

Thanks for the note 7stud. I think you can also write the above example
as:

class A
def foo
puts “foo from A”
end

def initialize
extend Redef
end
end

Gaspard B. wrote:

Any other solution ?

module Redef
def foo
puts “‘foo’ from module”
end
end

class A
def sayhi
puts “hi”
end

define_method(“foo”) do
puts “‘foo’ from define_method”
end

include Redef
end

class Wrapper
include Redef

def initialize(a_obj)
@a = a_obj
end

def method_missing(meth_name, *args)
@a.send(meth_name, *args)
end

end

a = A.new
w = Wrapper.new(a)

w.sayhi #hi
w.foo #‘foo’ from module
w.non_existent

r1test.rb:26:in send': undefined methodnon_existent’ for #<A:0x24928>
(NoMethodError)
from r1test.rb:26:in `method_missing’
from r1test.rb:35

Gaspard B. wrote:

Thanks for the note 7stud. I think you can also write the above example
as:

class A
def foo
puts “foo from A”
end

def initialize
extend Redef
end
end

Yes. I thought it might be clearer the other way.

Can anyone explain this one:

module Redef
def foo
puts “‘foo’ from module”
end
end

class A
define_method(“foo”) do
puts “‘foo’ from define_method”
end

def initialize
class << self
include Redef
end
end

#include Redef

end

p A.ancestors
a = A.new
a.foo

–output:–
[A, Object, Kernel]
‘foo’ from module

As expected, the version of foo in a’s singleton class is found before
the version of foo in class A in the method lookup before.

But what about here:

module Redef
def foo
puts “‘foo’ from module”
end
end

class A
define_method(“foo”) do
puts “‘foo’ from define_method”
end

def initialize
class << self
include Redef
end
end

include Redef #<—*****CHANGE HERE

end

p A.ancestors
a = A.new
a.foo

I would expect the order of the look up for the foo method to be:

a’s singleton class ==> ‘foo from module’
a’s class(=A) ==> ‘foo from define method’
a’s mixins(=Redef) ==> ‘foo from module’

But the output is:

[A, Redef, Object, Kernel]
‘foo’ from define_method

Hi –

On Mon, 14 Sep 2009, 7stud – wrote:

puts “‘foo’ from define_method”
end
a’s mixins(=Redef) ==> ‘foo from module’

But the output is:

[A, Redef, Object, Kernel]
‘foo’ from define_method

I believe what’s happening is this: when you include Redef in a’s
singleton class, Ruby sees that it’s already in the ancestors (since
it’s been included in A) and doesn’t add it.

In this example, I’ve got one module that’s included in A, and one
isn’t, and I extend my instance with both. As you can see, the Dummy
module appears before A in the ancestor list for a’s singleton class,
whereas Redef doesn’t.

module Redef
def foo
puts “‘foo’ from module”
end
end

module Dummy
end

class A
def foo
puts “‘foo’ from A”
end

def initialize
extend(Redef)
extend(Dummy)
end

include Redef

end

a = A.new
a.foo
p A.ancestors
class << a; p ancestors; end

Output:

‘foo’ from A
[A, Redef, Object, Kernel]
[Dummy, A, Redef, Object, Kernel]

David

Thanks.

On Sunday 13 September 2009 11:18:52 am Gaspard B. wrote:

end

Any other solution ?

As others said, drop the ‘define_method’. I’d take it a step further:

module A
module ClassMethods
def foo
puts “‘foo’ from A”
end
end
def self.included(base)
base.send :extend, ClassMethods
end
end

This is a common idiom. It’s not so much load order as the fact that
class
methods don’t automatically get included by “include” – but they are
also
instance methods on the class, if that makes sense.

The advantage of doing it this way is that you can put a lot more stuff
in
ClassMethods, or even mix in other modules (via “include”) inside
ClassMethods. It’s also nice and self-documenting, and it’s used all
over the
place – I believe inside Rails, at least.

I would even go so far as to call this a best practice. Thoughts?

I think your example does not correspond to our problem: with extend you
are adding class methods to the included class and our problem was
overwriting instance methods. In fact to avoid the “define_method”
inside the class priority thing this could be a better option by using
an anonymous Module:

class Base
def self.has_one(thing)
m = Module.new do
define_method(thing) do
puts “Has one #{thing}”
end
end
include m
end
end

module Redef
def dog
puts “Super says:”
super
puts “But I say: it has nothing because I decide so.”
end
end

class Dummy < Base
has_one :dog
include Redef
end

Dummy.new.dog

David M. wrote:

On Sunday 13 September 2009 11:18:52 am Gaspard B. wrote:

end

Any other solution ?

As others said, drop the ‘define_method’. I’d take it a step further:

module A
module ClassMethods
def foo
puts “‘foo’ from A”
end
end
def self.included(base)
base.send :extend, ClassMethods
end
end

This is a common idiom. It’s not so much load order as the fact that
class
methods don’t automatically get included by “include” – but they are
also
instance methods on the class, if that makes sense.

The advantage of doing it this way is that you can put a lot more stuff
in
ClassMethods, or even mix in other modules (via “include”) inside
ClassMethods. It’s also nice and self-documenting, and it’s used all
over the
place – I believe inside Rails, at least.

I would even go so far as to call this a best practice. Thoughts?

On Sun, Sep 13, 2009 at 2:44 PM, David A. Black [email protected]
wrote:

end
end
I would expect the order of the look up for the foo method to be:

I believe what’s happening is this: when you include Redef in a’s
singleton class, Ruby sees that it’s already in the ancestors (since
it’s been included in A) and doesn’t add it.

Yep.

Ruby will only include a module once in the implementation chain, it
turns out it’s because of the way the super keyword is implemented.

I talked to Matz about why this was at RubyConf in 2007 and wrote
about the conversation

http://talklikeaduck.denhaven2.com/2007/11/03/a-chat-with-matz-classs-variable-reversion-and-a-mystery-explained.


Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale

Hi –

On Tue, 15 Sep 2009, David M. wrote:

end
end
end
def self.included(base)
base.send :extend, ClassMethods

That’s the long way round :slight_smile:

base.extend(ClassMethods)

I would even go so far as to call this a best practice. Thoughts?

The name “ClassMethods” is potentially a bit confusing, since they’re
not exactly class methods… but I think it’s an effective way to do
what it does. I don’t think it’s inherently a better practice than,
say, extending a class explicitly in the class – meaning, I wouldn’t
go out of my way to set it up this way if it didn’t fall into place
fairly naturally in a give case.

David

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs