Trans
2/20/2009 2:12:00 PM
On Feb 20, 8:42=A0am, "David A. Black" <dbl...@rubypal.com> wrote:
> Hi --
>
>
>
> On Fri, 20 Feb 2009, Adam Wilson wrote:
> > 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
> > =A0def self.test
> > =A0 =A0"class"
> > =A0end
>
> > =A0def test
> > =A0 =A0"instance"
> > =A0end
> > end
>
> > class MyClass
> > =A0include MyModule
> > end
>
> > class MyController
> > =A0my_class =3D MyClass.new
>
> > =A0test1 =3D MyClass.test #=3D> gives an NoMethodError
> > =A0test2 =3D my_class.test #=3D> works
> > end
>
> > Is there something I am missing here? Please note I just wrote this cod=
e
> > 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:
>
> =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.