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?

···

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

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

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/

···

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

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

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

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

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

···

On Tue, Jul 22, 2008 at 8:07 PM, ara.t.howard <ara.t.howard@gmail.com> wrote:
A::new
A::new

----------------------------------

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.
--
Ernest Hemingway

James Coglan 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\).

···

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

Robert Dober 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.

···

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

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.
--
Ernest Hemingway

Robert Dober 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

···

On Fri, Jul 25, 2008 at 3:22 PM, Julien Thewys <jt@udev.org> wrote:

Robert Dober 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/\.

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

There's no one thing that's true. It's all true.
--
Ernest Hemingway

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 Jul 25, 2008, at 7:22 AM, Julien Thewys wrote:

I meant other than for hacking purpose.

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

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

···

On Fri, Jul 25, 2008 at 6:40 PM, ara.t.howard <ara.t.howard@gmail.com> wrote:

On Jul 25, 2008, at 7:22 AM, Julien Thewys 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/
--
we can deny everything, except that we have the possibility of being better.
simply reflect on that.
h.h. the 14th dalai lama

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

There's no one thing that's true. It's all true.
--
Ernest Hemingway