Simple module for "count my instances" behaviour

I want to make a simple module that makes its including classes
self-countable.
Here is the code (that does not work, incidentally):

#—code
module Countable
@@counter = 0

def initialize
@@counter += 1
end

module ClassMethods
def population
return @@counter
end
end

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

class User
include Countable
end

User.new
User.new
puts User.population
#—code

The error message says that the @@counter class variable is not
initialized in Countable::ClassMethods (so in the population method).
Obviously, this @@counter variable is not the same as the one that is
declared when including Countable, since User.new works as expected.

Can someone explain me why and how to solve this?

The error message says that the @@counter class variable is not
initialized in Countable::ClassMethods (so in the population method).
Obviously, this @@counter variable is not the same as the one that is
declared when including Countable, since User.new works as expected.

Can someone explain me why and how to solve this?

I think what you really want is an instance variable in the including
class,
since the counter is a property of the class. The instance variable must
be
created anew in every including class, whereas I think your code will
try to
reuse Countable’s @@counter variable for all classes. This code works:

module Countable
def initialize
self.class.instance_eval do
@counter ||= 0
@counter += 1
end
end

module ClassMethods
def population
return @counter || 0
end
end

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

class User
include Countable
end

User.new
User.new
puts User.population

On Tue, Jul 22, 2008 at 8:07 PM, ara.t.howard [email protected]
wrote:

Maybe there is a way to leave Class alone :wink:

This will track instances for all ancestors of InstanceTracker.

module InstanceTracker

Define a block that defines the included

inclusion = proc { | by_module |
singleton = class << by_module; self end

unless Class === by_module then
  singleton.send :define_method, :included, &inclusion
  return
end

by_module.instance_variable_set "@__instances__", []
singleton.send :define_method, :inherited, &inclusion
singleton.module_eval do
  def new *args, &blk
    o = allocate
    o.send( :initialize, *args, &blk )
    @__instances__ << o
    o
  end
  def instances; @__instances__ end
end

}

Define included in base module

class << self; self end.
send :define_method, :included, &inclusion
end # module InstanceTracker

M2 = Module::new {
include InstanceTracker
}
class A
include M2
end
B = Class::new A do
def initialize; 42 end
end

A::new
B::new
A::new
p A.instances
p B.instance


One could take special care of classes that define a custom new, but
that would make the code too complicated for the demonstrational
purpose I feel.

HTH
Robert


http://ruby-smalltalk.blogspot.com/

There’s no one thing that’s true. It’s all true.

On Jul 22, 2008, at 10:24 AM, Julien T. wrote:

I want to make a simple module that makes its including classes
self-countable.
Here is the code (that does not work, incidentally):

something like ?

cfp:~ > cat a.rb
class Class
New = method :new unless defined?(New)
Count = Hash.new{|h,k| h[k] = 0} unless defined?(Count)

def new *a, &b
New.call(*a, &b)
ensure
Count[self] += 1 unless $!
end

def count
Count[self]
end
end

class C; end

3.times{ C.new }
3.times{ Array.new }

p ‘C.count’ => C.count
p ‘Array.count’ => Array.count
p ‘Hash.count’ => Hash.count

cfp:~ > ruby a.rb
{“C.count”=>3}
{“Array.count”=>3}
{“Hash.count”=>0}

a @ http://codeforpeople.com/

Robert D. wrote:

One could take special care of classes that define a custom new, but
that would make the code too complicated for the demonstrational
purpose I feel.

In fact, the purpose of my example was educational.
Defining a custom new would illustrate how to be evil in my case :slight_smile:

By the way, I can not think of a legitimate use of redefining new.

By the way, I can not think of a legitimate use of redefining new.
Do you mean my code is not legitimate?
I think this is precisely a good use case.

Robert


http://ruby-smalltalk.blogspot.com/

There’s no one thing that’s true. It’s all true.

James C. wrote:

I think what you really want is an instance variable in the including
class,
since the counter is a property of the class. The instance variable must
be
created anew in every including class, whereas I think your code will
try to
reuse Countable’s @@counter variable for all classes. This code works:

Thanks James.
I was trying to illustrate the use of class variables with modules but,
as you pointed out, my example is precisely a case where we need class
instance variables
(http://www.martinfowler.com/bliki/ClassInstanceVariable.html).

On Fri, Jul 25, 2008 at 3:22 PM, Julien T. [email protected] wrote:

Robert D. wrote:

By the way, I can not think of a legitimate use of redefining new.
Do you mean my code is not legitimate?
I think this is precisely a good use case.

I meant other than for hacking purpose.

Posted via http://www.ruby-forum.com/.

Yes but that was my point, I guess if you wanted to expose this method
of tracking, you should check if your classes implement new for some
hacking reason ;). I would prefer the term “metaprogramming” though.
I just wanted to be sure that you are aware of that, if we redefine
new in a legitimate way, someone else might have.

Cheers
Robert


http://ruby-smalltalk.blogspot.com/

There’s no one thing that’s true. It’s all true.

On Jul 25, 2008, at 7:22 AM, Julien T. wrote:

I meant other than for hacking purpose.

there are many good reasons - one is for subclassing, if you override
new clients do not have to remember to call ‘super’

class C
def C.new *a, &b
(object = allocate).instance_eval do
init *a, &b
initialize *a, &b
self
end
end

def init
@you = ‘do not have to remember to call this’
end
end

class D < C
def initialize
end
end

another reason is for instance in the case of dike.rb, by re-defining
‘new’ it can track object creation, register a finalizer, and thereby
track the object lifecycle to detect memory leaks

regards.

a @ http://codeforpeople.com/

On Fri, Jul 25, 2008 at 6:40 PM, ara.t.howard [email protected]
wrote:

class C
end
it can track object creation, register a finalizer, and thereby track the

Ara I know your code is quite fine and demonstrating what you wanted
to, I am however still not sure that
I brought my point across :frowning:

The dilemma of a way to redefine new statically (1) or dynamically (2)
is to allow it to tolerate other new implementations in the
inheritance chain.

(1)
class C
def self.new *args, &blk
super(*args, &blk).instance_eval do

self
end
end
end

(2)

module M
def self.inherited a_mod
return if Class === a_mod
class << a_mod
alias_method :old, :new rescue nil
self
end.module_eval do
def new *args, &blk
old(args,&blk).instance_eval do

self
end

Cheers
Robert


http://ruby-smalltalk.blogspot.com/

There’s no one thing that’s true. It’s all true.

Robert D. wrote:

By the way, I can not think of a legitimate use of redefining new.
Do you mean my code is not legitimate?
I think this is precisely a good use case.

I meant other than for hacking purpose.