module include class/instance methods confusion!!

A

Adam Wilson

I have been having trouble today with a module include in a Rails app.
The module file sits in the /lib folder.

My problem, I worked out (which I have noticed before) is that I cannot
seem to access defined class methods in the module via the including
class. It seems that only instance methods are available. Let me
explain:

module MyModule
def self.test
"class"
end

def test
"instance"
end
end

class MyClass
include MyModule
end

class MyController
my_class = MyClass.new

test1 = MyClass.test #=> gives an NoMethodError
test2 = my_class.test #=> works
end

Is there something I am missing here? Please note I just wrote this code
quickly to show the idea, have not tested!
Its not essential but seems a bit silly that I need to use an instance
method in a situation when a class method would be much neater.

Any pointers much appreciated!
 
D

David A. Black

Hi --

I have been having trouble today with a module include in a Rails app.
The module file sits in the /lib folder.

My problem, I worked out (which I have noticed before) is that I cannot
seem to access defined class methods in the module via the including
class. It seems that only instance methods are available. Let me
explain:

module MyModule
def self.test
"class"
end

def test
"instance"
end
end

class MyClass
include MyModule
end

class MyController
my_class = MyClass.new

test1 = MyClass.test #=> gives an NoMethodError
test2 = my_class.test #=> works
end

Is there something I am missing here? Please note I just wrote this code
quickly to show the idea, have not tested!
Its not essential but seems a bit silly that I need to use an instance
method in a situation when a class method would be much neater.

Any pointers much appreciated!

If you have a spare six months, you can look through the previous
discussions of this topic on this list :)

Basically, the module and the class that includes it are independent
agents, when it comes to their own singleton/class methods. The
include operation is for the benefit of the objects that already
consult the class for methods: it adds the module to the method lookup
path of those objects. The class object itself is unaffected.

You can intercept the include event, though, and manipulate the lookup
path of the class object itself. A very common pattern is:

module M
def x
puts "Instance method"
end

module ClassMethods
def y
puts "Class method"
end
end

def self.included(c)
c.extend(ClassMethods)
end
end


class C
include M
end

C.new.x # Instance method
C.y # Class method

The idea here is to extend (i.e., do a per-object include) the class
object itself with a specialized module, M::ClassMethods, at the same
time that the module M is included in the class.

The name "ClassMethods", of course, is arbitrary; the methods defined
in that module are instance methods of the M::ClassMethods module.
They only become "class methods" by virtue of the extend operation.

I know it probably seems like a lot of work for something that you
might think should be automatic, but it's actually good that it isn't
automatic, because that would remove the ability to keep the singleton
(class) methods of a given module and a given class separate. This
way, with the out-of-the-box behavior being the more basic, there's
nothing you can't do.


David

--
David A. Black / Ruby Power and Light, LLC
Ruby/Rails consulting & training: http://www.rubypal.com
Coming in 2009: The Well-Grounded Rubyist (http://manning.com/black2)

Ruby Training Atlanta! April 1-3, http://www.entp.com/training/atlanta09
 
T

trans

Hi --













If you have a spare six months, you can look through the previous
discussions of this topic on this list :)

Basically, the module and the class that includes it are independent
agents, when it comes to their own singleton/class methods. The
include operation is for the benefit of the objects that already
consult the class for methods: it adds the module to the method lookup
path of those objects. The class object itself is unaffected.

You can intercept the include event, though, and manipulate the lookup
path of the class object itself. A very common pattern is:

=A0 =A0module M
=A0 =A0 =A0def x
=A0 =A0 =A0 =A0puts "Instance method"
=A0 =A0 =A0end

=A0 =A0 =A0module ClassMethods
=A0 =A0 =A0 =A0def y
=A0 =A0 =A0 =A0 =A0puts "Class method"
=A0 =A0 =A0 =A0end
=A0 =A0 =A0end

=A0 =A0 =A0def self.included(c)
=A0 =A0 =A0 =A0c.extend(ClassMethods)
=A0 =A0 =A0end
=A0 =A0end

=A0 =A0class C
=A0 =A0 =A0include M
=A0 =A0end

=A0 =A0C.new.x =A0 =A0 =A0 # Instance method
=A0 =A0C.y =A0 =A0 =A0 =A0 =A0 # Class method

The idea here is to extend (i.e., do a per-object include) the class
object itself with a specialized module, M::ClassMethods, at the same
time that the module M is included in the class.

The name "ClassMethods", of course, is arbitrary; the methods defined
in that module are instance methods of the M::ClassMethods module.
They only become "class methods" by virtue of the extend operation.

I know it probably seems like a lot of work for something that you
might think should be automatic, but it's actually good that it isn't
automatic, because that would remove the ability to keep the singleton
(class) methods of a given module and a given class separate. This
way, with the out-of-the-box behavior being the more basic, there's
nothing you can't do.

I think that's a fallacious argument.

First, I would like to see an example where it would really be a
problem. I've heard plenty of opinion on this, I've yet to see any
hard examples where it is actually a problematic limitation.

But besides that, why can't there be both? Let #include do it's thing
and have another method provide the other.

My argument for having the capability is that it provides single
encapsulation of concerns. I have come across plenty of situations
where the meta and instance level need to communicate. This works fine
if your need fits the profile of a subclass. But if you need a mixin,
then you are forced to encapsulate the two parts as separate modules,
even though they represent a single cohesive unit of logic. We end up
with extraneous separations of code with non-descriptive names, like
"ClassMethods", which is neither intuitive or naturally documenting.

T.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top