From: Robert Klemme Date: 2008-10-17T18:29:32+09:00 Subject: Re: instance counter On 17.10.2008 07:19, Mike Gold wrote: > Tommy Nordgren wrote: >> def Bob.count >> return @@count >> end > > I avoid @@. Why should metaclasses have a different syntax? @@ also > has different scoping rules which I find vexing. Be consistent, I say. > > class Bob > @count = 0 > > class << self > attr_reader :count > end > > def initialize > Bob.instance_eval { @count += 1 } > end > end > > a = Bob.new > b = Bob.new > puts Bob.count # => 2 > > And in general, > > def new_instance_counted_class > Class.new { > count = 0 > > (class << self ; self ; end).instance_eval { > define_method(:count) { > count > } > } > > define_method(:initialize) { > count += 1 > } > } > end > > class Bob < new_instance_counted_class > # ... > end > > a = Bob.new > b = Bob.new > puts Bob.count # => 2 > > To those who were asking about Class.new{} from one of my previous > posts, here is a good example. > > The local variable "count" lives in the closure, and is private in the > ultimate sense. There are no scoping rules to worry about, no efforts > are needed to choose a @hopefully_unused_variable_name, no surprising > subclass behavior, and no possibility that an instance_eval can rape the > count. > > Try writing the above using the conventional Java-like "blub" > constructs. You'll have some or all the problems I just mentioned. I nevertheless believe there is a more Rubyish solution to achieve what you did above with new_instance_counted_class: module InstanceCounted module InstanceCounter attr_accessor :count end def initialize(*a,&b) super self.class.count += 1 end def self.included(cl) cl.extend InstanceCounter cl.count = 0 end end class Foo include InstanceCounted end Note though that this approach (as yours above) has some shortcomings: the count is not adjusted when objects are garbage collected. And you must invoke super if you define Foo#initialize (a good habit anyway) - in your case you need to alias the old initialize and call that if you redefine it. There is another option though: you can get the current count via ObjectSpace: count = 0 ObjectSpace.each_object(Foo) { c += 1 } Kind regards robert