Not sure if I should be asking this on the core list instead, but... I've a
question about the included() and extended() hooks. Consider the following:
# A little helper from _why
class Object
def metaclass
class << self; self; end
end
end
# A module with hooks
module Hooks
def self.included(base)
puts "including M"
end
def self.extended(base)
puts "extending using M"
end
def includes_hooks?
true
end
end
# A couple classes
class Foo; end
class Bar; end
# Prints "extending using M"
Foo.extend(Hooks)
# Prints "including M"
Bar.metaclass.send(:include, Hooks)
# Both return true
Foo.includes_hooks?
Bar.includes_hooks?
So my question is: given that extend() is implemented (I think) using
metclass.include(), and that the end result of both is identical, how come
Hooks.included is not called when using extend()? It seems reasonable that
both the included() and extended() hooks should be called in this situation,
though I can just as well see the argument against doing this. Anyone care
to enlighten/persuade me as to why this behaviour exists?
So my question is: given that extend() is implemented (I think) using
metclass.include(), and that the end result of both is identical, how
come
Hooks.included is not called when using extend()? It seems reasonable
that
both the included() and extended() hooks should be called in this
situation,
though I can just as well see the argument against doing this. Anyone
care
to enlighten/persuade me as to why this behaviour exists?
In ruby everything is an object. So even class is an object.
Still we have extend and include which are not the same methods:
include => includes code into class.
extend => extends using module, appends class methods.
bar = Bar.new
bar - object of class Bar
bar.class - class of object bar
Bar - class Bar, object of class Class
Bar.metaclass - class of class Bar
Bar.metaclass.send(:include, Hooks)
is called on class of class Bar. So you got the methods as instance
methods of class Bar.
My explaination may not have sense for you. Better read Why’s Poignant
Guide to Ruby - chapter 6 and/or Dr. Nic Williams PDF from euruko
conference http://www.euruko2008.org/pages/show/2-program\.
I think you all kind of answered the wrong question.
James, I agree that it makes sense to regard "extend" as a special form
of "include". But that does in no way mean that it's actually
implemented like that. In fact, I'm pretty sure you won't find anything
equivalent to
def extend mod
singleton_class.send :include, mod
end
in the source code (written in C, of course).
Even on the semantic level I wouldn't necessarily expect the include
hook to be called. Nobody said that an "extend" actually *is* an
"include".
In 1.9.3 at least, Object#extend calls rb_mod_extend_object() which
just calls rb_extend_object() which is implemented as:
void
rb_extend_object(VALUE obj, VALUE module)
{
rb_include_module(rb_singleton_class(obj), module);
}
which bears out your hunch about the low-level implementation.
However, Object#extend and Module#include differ in that they call
different sets of hooks - #extend calls #extend_object and #extended
where #include calls #append_features and #included.
I guess #extend does not trigger a call to #included because it
enables you to distinguish the different use cases for a module (being
included into a class and extending a specific object).
You've shown that you can reproduce the effect of #extend by using #include directly on a singleton class but by including a module
directly into a class's singleton class you're going 'under the hood'
as it were (and note that you have to use #send(:include, Hooks) here
where you could have just used Bar.extend(Hooks) instead).
Given the different intended use cases for #include and #extend, it
does not strike me as anomalous that #extend does not trigger a call
to #included.
Regards,
Sean
···
On Mon, Sep 1, 2008 at 4:58 PM, James Coglan <jcoglan@googlemail.com> wrote:
So my question is: given that extend() is implemented (I think) using
metclass.include(), and that the end result of both is identical, how come
Hooks.included is not called when using extend()? It seems reasonable that
both the included() and extended() hooks should be called in this situation,
though I can just as well see the argument against doing this. Anyone care
to enlighten/persuade me as to why this behaviour exists?
So my question is: given that extend() is implemented (I think) using
metclass.include(), and that the end result of both is identical, how
come
Hooks.included is not called when using extend()? It seems reasonable
that
both the included() and extended() hooks should be called in this
situation,
though I can just as well see the argument against doing this. Anyone
care
to enlighten/persuade me as to why this behaviour exists?
In ruby everything is an object. So even class is an object.
Still we have extend and include which are not the same methods:
include => includes code into class.
extend => extends using module, appends class methods.
include and extend both do the same thing: they add a module to the
method lookup path of an object or set of objects. So, for example:
class C
end
c = C.new
c.talk # looks in C, Object, Kernel
# no 'talk' method found: error
module M
def talk
puts "hi"
end
end
class C
include M
end
c.talk # looks in C, M ...stops there because it finds 'talk'
No code is added to C. When you use extend, you're doing an "include"
operation on the object's singleton class. So:
str = "hello"
str.extend(M)
str.talk # looks in its singleton class, then M...
# and finds 'talk'
If you do, for example:
C.extend(M)
then you're adding M to the lookup path of C. That means you can do:
C.talk
But note that it's exactly the same pattern as before.
bar = Bar.new
bar - object of class Bar
bar.class - class of object bar
Bar - class Bar, object of class Class
Bar.metaclass - class of class Bar
The class of class Bar is Class. The singleton class of Bar isn't the
same as the class of Bar.
David
···
On Tue, 2 Sep 2008, Maciej Tomaka wrote:
--
Rails training from David A. Black and Ruby Power and Light:
Intro to Ruby on Rails January 12-15 Fort Lauderdale, FL
Advancing with Rails January 19-22 Fort Lauderdale, FL *
* Co-taught with Patrick Ewing!
See http://www.rubypal.com for details and updates!