Mixing in class methods

Okay, probably a dumb question, but: is there any way to define
methods in a module which become class methods of any class which
includes that module?

For instance:

module Foo;
class << self
def foo; puts "foo!"; end
end
end

class Bar
include Foo
end

Bar.foo

NoMethodError: undefined method `foo' for Bar:Class
	from (irb):11

-Mark

Close:

module M
  def foo
    p "Hello world"
  end
end

class C
end

class << C
  include M
end

C.foo   # => "Hello world"

hth,

···

On Thu, 2 Oct 2003 06:02:32 +0900 “Mark J. Reed” markjreed@mail.com wrote:

Okay, probably a dumb question, but: is there any way to define
methods in a module which become class methods of any class which
includes that module?


Ryan Pavlik rpav@mephle.com

“I’ve got a crude stabbing implement right here with
your name all over it.” - 8BT

Alternatively,

class C
extend M
end

C.foo # => “Hello world”

Gavin

···

On Thursday, October 2, 2003, 7:08:00 AM, Ryan wrote:

On Thu, 2 Oct 2003 06:02:32 +0900 > “Mark J. Reed” markjreed@mail.com wrote:

Okay, probably a dumb question, but: is there any way to define
methods in a module which become class methods of any class which
includes that module?

Close:

module M
  def foo
    p "Hello world"
  end
end
class C
end
class << C
  include M
end
C.foo   # => "Hello world"

Okay, those both work, assuming all I want is class methods from the module.
I’m guessing I have to break it up. The original idea was to have a
single module, which defined both class and instance methods, which I could
include/extend/whatever into a class, and then that class would have both
the instance methods (as instance methods) and the class methods (as
class methods) from the module.

-Mark

···

On Thu, Oct 02, 2003 at 07:37:25AM +0900, Gavin Sinclair wrote:

On Thursday, October 2, 2003, 7:08:00 AM, Ryan wrote:

class << C
  include M
end

Alternatively,

class C
extend M
end

I’ve used something like this before:

module M
def self.append_features(klass)
class << klass
def class_method
p “class_method”
end
end
super
end

def instance_method
  p "instance_method"
end

end

class C
include M
end

C.class_method
C.new.instance_method

Gotta love that Ruby!

Nathaniel

<:((><

···

Mark J. Reed [mailto:markjreed@mail.com] wrote:

Okay, those both work, assuming all I want is class methods
from the module. I’m guessing I have to break it up. The
original idea was to have a single module, which defined both
class and instance methods, which I could
include/extend/whatever into a class, and then that class
would have both the instance methods (as instance methods)
and the class methods (as class methods) from the module.

Yes, it’s too much to ask :slight_smile: Ruby doesn’t know the difference between
instance and class methods. Every method is an instance method, it’s just
that some instances happen to be classes.

Note that “C.extend(M)” works as well, just like “(s = ‘’).extend(M)”.

It sometimes makes me wonder why Ruby differentiates between instance
variables and class variables. The latter seem exceptional, for a number
of reasons.

Gavin

···

On Thu, Oct 02, 2003 at 07:37:25AM +0900, Gavin Sinclair wrote:

On Thursday, October 2, 2003, 7:08:00 AM, Ryan wrote:

class << C
  include M
end

Alternatively,

class C
extend M
end

Okay, those both work, assuming all I want is class methods from the
module. I’m guessing I have to break it up. The original idea was to
have a single module, which defined both class and instance methods,
which I could include/extend/whatever into a class, and then that class
would have both the instance methods (as instance methods) and the class
methods (as class methods) from the module.

-Mark

Perfect! I keep forgetting that most of the innards of Ruby are
themselves Ruby. Darn handy, that. :slight_smile:

Thanks!

-Mark

···

On Thu, Oct 02, 2003 at 10:26:48AM +0900, Nathaniel Talbott wrote:

I’ve used something like this before:

module M
def self.append_features(klass)
class << klass
def class_method
p “class_method”
end
end
super
end

~/eg/ruby > cat classvar.rb
class A
@@a = 42 # i am a class var, and can be inherited
@a = 42 # i belong to THIS instance of a class, class A
end

class B < A
printf “@@a = <%s>\n”, (@@a or ‘nil’)
printf “@a = <%s>\n”, (@a or ‘nil’)
end

~/eg/ruby > ruby classvar.rb
@@a = <42>
@a =

-a

···

On Thu, 2 Oct 2003, Gavin Sinclair wrote:

It sometimes makes me wonder why Ruby differentiates between instance
variables and class variables. The latter seem exceptional, for a number of
reasons.

====================================

Ara Howard
NOAA Forecast Systems Laboratory
Information and Technology Services
Data Systems Group
R/FST 325 Broadway
Boulder, CO 80305-3328
Email: ara.t.howard@noaa.gov
Phone: 303-497-7238
Fax: 303-497-7259
The difference between art and science is that science is what we understand
well enough to explain to a computer. Art is everything else.
– Donald Knuth, “Discover”
~ > /bin/sh -c ‘for lang in ruby perl; do $lang -e “print "\x3a\x2d\x29\x0a"”; done’
====================================

“Nathaniel Talbott” wrote:

module M
def self.append_features(klass)
class << klass
def class_method
p “class_method”
end
end
super
end

Actually the append_feature + super trick is somewhat
outdated in 1.8.0 - use #included instead.

···

module M
end

class << M
module ClassMethods # this avoids name pollution
def class_method1
p “class_method”
end
def class_method2
p “class_method2”
end
end
def included(mod)
if mod.is_a? Class
mod.extend ClassMethods
else
# maybe raise an exception ?
raise “bla …”
end
end
end

class C
include M
end

/Christoph

It sometimes makes me wonder why Ruby differentiates between instance
variables and class variables. The latter seem exceptional, for a
number of reasons.

[Ara:]

~/eg/ruby > cat classvar.rb
class A
@@a = 42 # i am a class var, and can be inherited
@a = 42 # i belong to THIS instance of a class, class A
end

class B < A
printf “@@a = <%s>\n”, (@@a or ‘nil’)
printf “@a = <%s>\n”, (@a or ‘nil’)
end

~/eg/ruby > ruby classvar.rb
@@a = <42>
@a =

Yeah, I know the theory (but thanks for summarising anyway), but I haven’t
comfortably fit it in to my programming arsenal. e.g. recently I had some
code like this.

class Project
@@implemented_methods =

def implement(_method)
  # some checking
  yield
  # some checking
  @@implemented_methods << _method
end

def Project.implemented_methods
  @@implemented_methods
end

end

So, what do I consider “exceptional”? Some comments:

  1. Accessing a class variable is nowhere near as convenient as
    accessing an instance variable.

  2. “class methods” and “instance methods” are the same concept
    (since classes are objects), but there’s nothing to unify
    “class variables” and “instance variables”. Observe that

    a) class variables (@@example) can be resolved whether “self”
    represents the class or the instance

    b) “class methods”, unlike in Java (static methods), can’t be
    directly accessed from the instance

  3. Although “class instance variables” are a logical entity,
    they do not appear to be a common programming idiom.

I initially tried to code my example like this:

class Project
class << Project
attr_accessor :implemented_methods
end

def implement(_method)
  # some checking
  yield
  # some checking
  Project.implemented_methods << _method
end

end

Note in both examples, the idea is to be able to call

Project.implemented_methods

after calling Project#implement a number of times. However, the second
case (using a class instance variable instead of a class variable) did not
work. I think the array always came back empty. Now I may have done
something simple wrong, but I think I’ve demonstrated enough anyway. :slight_smile:
Well, I hope so.

Cheers,
Gavin

“Christoph” <swap(news_chr)@gmx.net> schrieb im Newsbeitrag
news:blgajs$30q6$1@ulysses.news.tiscali.de

“Nathaniel Talbott” wrote:

module M
def self.append_features(klass)
class << klass
def class_method
p “class_method”
end
end
super
end

Actually the append_feature + super trick is somewhat
outdated in 1.8.0 - use #included instead.


module M
end

class << M
module ClassMethods # this avoids name pollution
def class_method1
p “class_method”
end
def class_method2
p “class_method2”
end
end
def included(mod)
if mod.is_a? Class
mod.extend ClassMethods
else
# maybe raise an exception ?
raise “bla …”
end
end
end

class C
include M
end

This looks a bit complicated to me. IMHO you achieve the same with this
much shorter version_

module M
def class_method1
p “class_method”
end
def class_method2
p “class_method2”
end
end

class C
extend M
end

alternatively:

class C
class << self
include M
end
end

Regards

robert

Hi –

I initially tried to code my example like this:

class Project
class << Project
attr_accessor :implemented_methods
end

def implement(_method)
  # some checking
  yield
  # some checking
  Project.implemented_methods << _method
end

end

Note in both examples, the idea is to be able to call

Project.implemented_methods

after calling Project#implement a number of times. However, the second
case (using a class instance variable instead of a class variable) did not
work. I think the array always came back empty. Now I may have done
something simple wrong, but I think I’ve demonstrated enough anyway. :slight_smile:
Well, I hope so.

Ummm, what array? :slight_smile: I think you’d need something like:

class Project
class << self
attr_reader :implemented_methods
def implemented_methods
@method_list ||=
end
end

def implement(method)
  Project.implemented_methods << method
end

end

David

···

On Thu, 2 Oct 2003, Gavin Sinclair wrote:


David Alan Black
home: dblack@superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

Yes, but what if you add a ‘def instance_method1’ to M? Class C
doesn’t get it via either approach you cite; it shows up as a class method
instead. All of this circumlocution has been to allow a module to
define BOTH class AND instance methods.

Thanks to all. The :append_features solution was giving me trouble
with modules that included other modules; :included may help there.

-Mark

···

On Thu, Oct 02, 2003 at 10:51:52AM +0200, Robert Klemme wrote:

This looks a bit complicated to me. IMHO you achieve the same with this
much shorter version_

module M
def class_method1
p “class_method”
end
def class_method2
p “class_method2”
end
end

“Robert Klemme” wrote:

module M
def class_method1
p “class_method”
end
def class_method2
p “class_method2”
end
end

class C
extend M
end

Hm, just being my silly self, what about the following
solution for the combined include + extend problem?

···

class WiredModule < Module
private
def extend_object(obj)
super
extended(obj)
end
def extended(obj)
end
end

ClassMethods = WiredModule.new do
def class_method1
p “class_method1”
end
def class_method2
p “class_method2”
end
end

class << ClassMethods
module M
def instance_method1
p “instance_method1”
end
end
def extended(mod)
mod.instance_eval { include M }
end
end

class C
extend ClassMethods
end

C.new.instance_method1 # “instance_method1”
C.class_method1 # “class_method1”

/Christoph

The assymetry between class/instance variables and class/instance
methods has always bothered me. It seems to me that it would
make more sense for class variables to be the same as instance
varaibles of the class object. That is:

class Foo
@@var = 1
class << self
# @var here should refer to @@var

end
end

This was, indeed, what I expected to be the case upon my first
exposure to these Ruby concepts. But instead, there are effectively
three categories of variable instead of two, and the @@ category seems to
me to behave very oddly.

Guess this is just another case where the principle-of-least-surprise-to-Matz
is not the same as the principle-of-least-surprise-to-Mark. :slight_smile:

-Mark

“Mark J. Reed” wrote:

Thanks to all. The :append_features solution was giving me trouble
with modules that included other modules; :included may help there.

On a serious note: The following might give you an idea on
dealing with the ``include Modules in other Module’’ issue.

···

class MModule < Module # the name is arbitrary
def initialize
@class_module = Module.new
super
end
private
def class_methods(&b)
@class_module.module_eval &b
end
def included(mod)
if mod.is_a? Class
mod.extend @class_module
else mod.is_a? MModule
mod.class_module.send(:include,@class_module)
end
end
protected
attr_reader :class_module
end

usage

M = MModule.new do
def instance_method1
p “instance_method1”
end
class_methods do
def class_method1
p “class_method1”
end
end
end

a MModule constant can be used like regular Module constant

module M
def instance_method2
p “instance_method2”
end
class_methods do
def class_method2
p “class_method2”
end
end
end

include example

N = MModule.new do
include M
def instance_method3
p “instance_method1”
end

class_methods do
def class_method3
p “class_method3”
end
end
end

class C
include N
end

C.new.instance_method1 # instance_method1
C.new.instance_method2 # instance_method2
C.new.instance_method3 # instance_method3
C.class_method1 # class_method1
C.class_method2 # class_method2
C.class_method3 # class_method3

/Christoph

This was, indeed, what I expected to be the case upon my first
exposure to these Ruby concepts. But instead, there are effectively
three categories of variable instead of two, and the @@ category seems to
me to behave very oddly.

No, no : there are only 2 categories.

Guy Decoux

Yes, I know: @ and @@. @ variables are instance variables of an object, and
classes are really just objects. But the is-a relationship is special,
and that specialness is echoed in the treatment of @@ variables.

Logically, I still think it would make sense for @@vars to be
equivalent to @vars of the class. The real problem is that it
would violate encapsulation within the middle-layer implementation -
instances need to be shielded from the innards of their class objects.

-Mark

···

On Thu, Oct 02, 2003 at 11:09:32PM +0900, ts wrote:

No, no : there are only 2 categories.

Hi –

No, no : there are only 2 categories.

Yes, I know: @ and @@. @ variables are instance variables of an
object, and classes are really just objects. But the is-a
relationship is special, and that specialness is echoed in the
treatment of @@ variables.

Logically, I still think it would make sense for @@vars to be
equivalent to @vars of the class. The real problem is that it
would violate encapsulation within the middle-layer implementation -
instances need to be shielded from the innards of their class objects.

Also, every object should have the right to maintain state in instance
variables that are not (easily) visible to other objects. Class
objects should not be penalized in this regard (i.e., by having their
instance variables conflated with something else) just because they
are Class objects.

Another way to look at it is that Matz clearly wants these two very
different things to exist, so if @@var were an alias for class <<
self; @var; end, there would presumably be something else ($$var or
^^var or whatever) to represent class variables. So then the question
would arise again: should ^^var be equivalent to class << self; @var;
end, and so on infinitely. I guess the @-similarity leads more quickly
to this kind of question than might be the case otherwise, but it’s
really just a cosmetic detail.

David

···

On Thu, 2 Oct 2003, Mark J. Reed wrote:

On Thu, Oct 02, 2003 at 11:09:32PM +0900, ts wrote:


David Alan Black
home: dblack@superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

Hi –

···

On Fri, 3 Oct 2003 dblack@superlink.net wrote:

Another way to look at it is that Matz clearly wants these two very
different things to exist, so if @@var were an alias for class <<
self; @var; end, there would presumably be something else ($$var or
^^var or whatever) to represent class variables. So then the question
would arise again: should ^^var be equivalent to class << self; @var;
end, and so on infinitely. I guess the @-similarity leads more quickly
to this kind of question than might be the case otherwise, but it’s
really just a cosmetic detail.

And of course the fact that it might actually be self.class.class_eval
{ @var } also reminds us that there’s no natural mapping between
these things :slight_smile:

David


David Alan Black
home: dblack@superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav