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: gist:186219 · GitHub
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.
Example ruby code: gist:186219 · GitHub
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
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.
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”.
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”.
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
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]
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
end
end
end
def self.included(base)
base.send :extend, ClassMethods
That’s the long way round
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.