Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can a class in ruby store the number of instantiated objects using a @class_instance_variable and not a @@class_variable?

I am trying to keep count of instances of objects of a given class, inside the class that defines those objects.

First of all I am aware of code reflection and ObjectSpace.each_object, but I'd prefer not to use reflection and let the class itself be able to "look after" itself.

I looked around and all solutions I found seem to make use of @@class_variables in the class definition like, for example, the accepted answer to this question: How to get class instances in Ruby?

As I kept reading around though, I found that class variables in ruby can behave very bad in some situations... biggest reason being this:

A class variable defined at the top‐level of a program is inherited by all classes. It behaves like a global variable.

source & more info here: http://ruby.runpaint.org/variables#class

So, I tried to code a class that stores the number of its instantiated objects inside itself using a class instance variable instead of a class variable, and apparently it seems to work ok but since I am still learning about this "in-deep" language topics I'd like to ask you if the code I wrote is correct and/or makes sense:

class Pizza
  @orders = 0
  def self.new
    @orders += 1
  end
  def self.total_orders
    @orders
  end
end

new_pizza = Pizza.new #=> 1
special_pizza = Pizza.new #=> 2
fav_pizza = Pizza.new #=> 3

One of my doubts is that overriding Pizza.new method i "delete" some important funcionality of the original .new method ? (What does the original .new method provide?? How can i "inspect" into a method code using reflection?) What else am I doing wrong, or totally wrong in my approach and why ?

Thanks

edit: forgot to add this important bit:

As a way to better constrain the scope of things, I'd like the Pizza class to be able to count only at object instantiation time and not to have a setter method on its @instance class variable, which can be accessed at anytime in code (Pizza.count = 1000). That's why I was trying to override "new".

I think this is the trickiest part which makes me ask myself is my approach is in the right direction, or if I simply should stop reling so much on these language mechanisms and instead add myself some logic to let the counting happen only if a object of a Pizza class has entered ObjectSpace..

I am just looking for the more elegant, non bloated way to obtain this using language features..

In either case help would be appreciated..

Thanks again.

like image 694
JfredoJ Avatar asked Oct 15 '12 04:10

JfredoJ


2 Answers

You do not need to override new. Try this:

class Pizza
  @count = 0
  class << self
    attr_accessor :count
  end

  def initialize
    self.class.count += 1
  end
end

Pizza.new
Pizza.new
puts Pizza.count

Please note that Pizza.count will not go down when the pizzas are garbage collected, and it will not go up when they are duped.

To answer your other questions: I'm not sure how you can inspect the new method, but it is probably written in C so you should look in the Ruby source code. I think it basically does this.

def new(*args, &block)
  o = allocate
  o.initialize(*args, &block)
  o
end

You could probably get away with overriding new as long as you called super at some point. (Not saying this is a good idea, but for example:

class Foo
  def self.new
    # do whatever stuff you want here
    super
  end
end

)

like image 81
David Grayson Avatar answered Sep 20 '22 01:09

David Grayson


you are right. using class variables is mind boggling some times. i always forget if the get inherited or not etc.

your approach is right, but...

NEVER override new implement initialize instead! it will get called during the init process of every object. see the docs here: http://apidock.com/ruby/v1_9_3_125/Class/new

like image 41
phoet Avatar answered Sep 19 '22 01:09

phoet