Forum: Ruby instance counter

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
066f204e98bf9e8e4c4c48939483879c?d=identicon&s=25 Pierre Lebrun (anthonylebrun)
on 2008-10-17 05:37
Please forgive the trite nature of this question, I'm new to ruby and OO
programming in general. I'm looking to create a counter that keeps track
of the number of instances created from one class (++ would we access
this from the class or the instace?).

I've made a few lame attempts myself so far but nothing functional :-P

Also, why does ruby return an error for:

Class Bob
  attr_reader :count
  @count = 0
  def initialize
    @count += 1
  end
end

Cheers!
A44df35268bd9586ce9328f0fe0313ec?d=identicon&s=25 Owein Herrmann (owein)
on 2008-10-17 05:49
Pierre Lebrun wrote:
> Also, why does ruby return an error for:
> Class Bob

keyword 'class' must be lowercase, not capitalized.

also, @count is an instance variable. In order to get a variable for
which only one copy exists for the class, use @@count

class Bob
  @@count=0
  def initialize
    @@count+=1
  end
  def Bob.count
    @@count
  end
end

a = Bob.new
b = Bob.new
puts Bob.count
#=> 2
~Owein
833348fb139ec4d62508eb9f0e28ac06?d=identicon&s=25 Tommy Nordgren (Guest)
on 2008-10-17 05:50
(Received via mailing list)
On Oct 17, 2008, at 5:36 AM, Pierre Lebrun wrote:

>
> Posted via http://www.ruby-forum.com/.
>
  You need to write @@count = 0.
and
def Bob.count
  return @@count
end
066f204e98bf9e8e4c4c48939483879c?d=identicon&s=25 Pierre Lebrun (anthonylebrun)
on 2008-10-17 06:11
Excellent, thanks for the classy help!
A246f7c0ce5f2909483d358bd9e83e4e?d=identicon&s=25 Mike Gold (mikegold)
on 2008-10-17 07:19
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.
833348fb139ec4d62508eb9f0e28ac06?d=identicon&s=25 Tommy Nordgren (Guest)
on 2008-10-17 09:43
(Received via mailing list)
On Oct 17, 2008, at 7:19 AM, 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.
>
  An instance counter have to be assosiated with the class itself, and
not
an instance. How else can it count the total number of allocated
instances?
You plainly did not read the original posters message.
> ------------------------------------------------------

"Home is not where you are born, but where your heart finds peace" -
Tommy Nordgren, "The dying old crone"
tommy.nordgren@comhem.se
E088bb5c80fd3c4fd02c2020cdacbaf0?d=identicon&s=25 Jesús Gabriel y Galán (Guest)
on 2008-10-17 10:22
(Received via mailing list)
On Fri, Oct 17, 2008 at 9:41 AM, Tommy Nordgren
<tommy.nordgren@comhem.se> wrote:
>> has different scoping rules which I find vexing.  Be consistent, I say.
>>
>        An instance counter have to be assosiated with the class itself, and
> not
> an instance. How else can it count the total number of allocated instances?
> You plainly did not read the original posters message.

A Class is also an object that can have its own instance variables,
which are different
from the instance variables of instances of that class. People usually
discourage the
use of class variables (@@x) because they have "weird" semantics when it
comes
to inheritance, and using class instance variables is usually better
and covers most
cases:

irb(main):001:0> class B
irb(main):002:1> @count = 0
irb(main):003:1>  class << self
irb(main):004:2> attr_accessor :count
irb(main):005:2> end
irb(main):006:1> attr_accessor :count
irb(main):007:1> def initialize
irb(main):008:2> B.count += 1
irb(main):009:2> end
irb(main):010:1> end
=> nil
irb(main):011:0>
irb(main):012:0*
irb(main):013:0* B.count
=> 0
irb(main):014:0> b = B.new
=> #<B:0xb7b77188>
irb(main):015:0> B.count
=> 1
irb(main):016:0> b.count
=> nil
irb(main):017:0> b.count = 55
=> 55
irb(main):018:0> b.count
=> 55
irb(main):019:0> B.count
=> 1

So, B.count and b.count are different things. B.count accesses an
instance variable of B, while b.count accesses instance variables of B
instances.

Jesus.
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2008-10-17 11:33
(Received via mailing list)
On 17.10.2008 07:19, Mike Gold wrote:
>
> b = Bob.new
>         count
>   # ...
> 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
7a561ec0875fcbbe3066ea8fe288ec77?d=identicon&s=25 Sebastian Hungerecker (Guest)
on 2008-10-17 14:59
(Received via mailing list)
Tommy Nordgren wrote:
> > I avoid @@.  Why should metaclasses have a different syntax?  @@ also
> > has different scoping rules which I find vexing.  Be consistent, I  
> > say.
>
>         An instance counter have to be assosiated with the class itself,
> and   not
> an instance. How else can it count the total number of allocated  
> instances?
> You plainly did not read the original posters message.

It seems to me that he did read the OP, but you did not fully read his
message. His code defines an instance variable on the class, so it *is*
associated with the class and not with any particular instance of that
class.

HTH,
Sebastian
A246f7c0ce5f2909483d358bd9e83e4e?d=identicon&s=25 Mike Gold (mikegold)
on 2008-10-19 21:46
Robert Klemme wrote:
>> 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

I said that one would have some or all the problems I mentioned, not
that it couldn't be done.  Indeed since you've made an attr_accessor,
anyone is free to modify count.  Had you used attr_reader instead,
anyone could still modify it with instance_eval (as in my first
example).  Of more importance is the looming conflict with @count.

I've spent too much time tracking down the bewildering bugs caused by
naming conflicts.  No doubt others will think, "Oh, it's so unlikely,
don't bother."  I used to believe that too.  Now I am more careful.  I
remove all these problems with one fell swoop.
4828d528e2e46f7c8160c336eb332836?d=identicon&s=25 Robert Heiler (shevegen)
on 2008-10-19 22:25
> I've spent too much time tracking down the bewildering bugs caused by
> naming conflicts.

I once had a bug caused by my misuse of @@ when there was no need for
it.

Since then I have become more sceptical - i like every feature I can
use, but when I can achieve something without using something, i.e. @@
all the better for my poor little brain. I would even go as far as to
claim in 95% of the cases, there is no real need to use @@ at all.
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2008-10-20 07:54
(Received via mailing list)
2008/10/19 Marc Heiler <shevegen@linuxmail.org>:
>> I've spent too much time tracking down the bewildering bugs caused by
>> naming conflicts.
>
> I once had a bug caused by my misuse of @@ when there was no need for
> it.
>
> Since then I have become more sceptical - i like every feature I can
> use, but when I can achieve something without using something, i.e. @@
> all the better for my poor little brain. I would even go as far as to
> claim in 95% of the cases, there is no real need to use @@ at all.

Absolutely agree!  Class variables are one of the rare spots of Ruby
where it does not shine at all IMHO.

Kind regards

robert
50b2daf0e7666574579b9edaf8f2b69a?d=identicon&s=25 Pit Capitain (Guest)
on 2008-10-20 09:03
(Received via mailing list)
2008/10/20 Robert Klemme <shortcutter@googlemail.com>:
> 2008/10/19 Marc Heiler <shevegen@linuxmail.org>:
>> I would even go as far as to
>> claim in 95% of the cases, there is no real need to use @@ at all.
>
> Absolutely agree!  Class variables are one of the rare spots of Ruby
> where it does not shine at all IMHO.

No, I think they are absolutely useful in the (rare) cases when you
need exactly what they offer compared to class instance variables. The
only thing that might be the source for problems is their name: too
many people associate something different with "class variables".

Regards,
Pit
47b1910084592eb77a032bc7d8d1a84e?d=identicon&s=25 Joel VanderWerf (Guest)
on 2008-10-20 19:32
(Received via mailing list)
Pit Capitain wrote:
> many people associate something different with "class variables".
>
> Regards,
> Pit

You're right. They should have an ungainly name, like "class tree
variables", which would help to discourage their use, except in those
rare cases.
5d06917e13b29bcff1c1609492c06873?d=identicon&s=25 Dave Thomas (Guest)
on 2008-10-20 19:46
(Received via mailing list)
On Oct 20, 2008, at 12:30 PM, Joel VanderWerf wrote:

> You're right. They should have an ungainly name, like "class tree
> variables", which would help to discourage their use, except in
> those rare cases.

I personally have stopped using them. When I need the functionality, I
use a class instance variable and a class-level attr_accessor to make
it available to instances.


Dave
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2008-10-20 21:00
(Received via mailing list)
2008/10/20 Pit Capitain <pit.capitain@gmail.com>:
> only thing that might be the source for problems is their name: too
> many people associate something different with "class variables".

Do you have an example which cannot be handled as easily by a class
instance variable?  I can't think of any ATM - which does not mean it
does not exist.

Kind regards

robert
50b2daf0e7666574579b9edaf8f2b69a?d=identicon&s=25 Pit Capitain (Guest)
on 2008-10-20 23:22
(Received via mailing list)
2008/10/20 Robert Klemme <shortcutter@googlemail.com>:
> Do you have an example which cannot be handled as easily by a class
> instance variable?  I can't think of any ATM - which does not mean it
> does not exist.

In one example I remember I had a class inheritance hierarchy and
needed to keep some information in the base class. I wanted to access
this information from the subclasses without having to know the exact
ancestor where the information was stored. The easiest way to do that
was using a class variable.

Of course it would also be possible to use class instance variables
and accessor methods like Dave suggested, but I didn't want to write
"BaseClass.accessor_method".

Regards,
Pit
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2008-10-21 08:05
(Received via mailing list)
On 20.10.2008 23:20, Pit Capitain wrote:
>
> Of course it would also be possible to use class instance variables
> and accessor methods like Dave suggested, but I didn't want to write
> "BaseClass.accessor_method".

class Base
   class <<self
     attr_accessor :foo
   end

   def foo
     Base.foo
   end

   def foo=(x)
     Base.foo=x
   end
end

class A < Base
end

class B < Base
end

B.new.foo=123
puts A.new.foo

If you need that more frequently you can even put this into a module
which takes care of the rest.  Or you define an instance method on
Module which sets up this scenario.

Kind regards

  robert
47b1910084592eb77a032bc7d8d1a84e?d=identicon&s=25 Joel VanderWerf (Guest)
on 2008-10-21 10:02
(Received via mailing list)
Robert Klemme wrote:
>     Base.foo=x
> puts A.new.foo
Then you need an instance to set a class attribute. What about this?
(Instance methods would be easy to add.)

class Base
   def self.foo
     if self == Base
       @foo
     else
       Base.foo
     end
   end

   def self.foo=(x)
     if self == Base
       @foo = x
     else
       Base.foo = x
     end
   end
end

class A < Base
end

class B < Base
end

B.foo=123
puts A.foo
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2008-10-21 10:18
(Received via mailing list)
2008/10/21 Joel VanderWerf <vjoel@path.berkeley.edu>:
>>
>>
>> B.new.foo=123
>> puts A.new.foo
>
> Then you need an instance to set a class attribute.

I thought this was what you wanted since you can use @@foo inside
instances.

>  end
>
>  def self.foo=(x)
>    if self == Base
>      @foo = x
>    else
>      Base.foo = x
>    end
>  end
> end

Yep, that makes access without instances easier.

Anyway, the point was to demonstrate that there is a clear and easy
solution that does not need class variables.  Whether it's mine or
yours does not really matter that much. I for my part would rather
have this solution (which is probably a bit more verbose than the
class variable solution) then resort to class variables because they
make code fragile because the declaration order matters.

Kind regards

robert
50b2daf0e7666574579b9edaf8f2b69a?d=identicon&s=25 Pit Capitain (Guest)
on 2008-10-21 11:26
(Received via mailing list)
2008/10/21 Robert Klemme <shortcutter@googlemail.com>:
> 2008/10/21 Joel VanderWerf <vjoel@path.berkeley.edu>:
>> Then you need an instance to set a class attribute.
>
> I thought this was what you wanted since you can use @@foo inside instances.

I needed it from the class level, so Joel's solution would be what I
needed.

> Anyway, the point was to demonstrate that there is a clear and easy
> solution that does not need class variables.  Whether it's mine or
> yours does not really matter that much. I for my part would rather
> have this solution (which is probably a bit more verbose than the
> class variable solution) then resort to class variables because they
> make code fragile because the declaration order matters.

I guess we have to agree to disagree here. To me the code shown was
neither more easy nor more clear than using class variables, but to
each his own. The declaration order of class variables didn't matter
in my case. (In fact I think you have to invent some very special load
orders and class variable creation code to be hitten by this problem
in reality.)

Regards, and thank you and Joel for showing some alternatives,
Pit
This topic is locked and can not be replied to.