Inheritance problem

I’m bad when I try to explain with my words, so I’ll just post code:

#--------------------------------------------------------
class Vehicle
attr_accessor :test

@@array_of_instances = []

def self.array_of_instances
@@array_of_instances
end

def initialize(t)
@test = t
@@array_of_instances << self
end
end

class Car < Vehicle
end

vehicle1 = Vehicle.new(“truck”)
vehicle2 = Vehicle.new(“ship”)

car1 = Car.new(“Peugeot”)

puts "Vehicles: " + Vehicle.array_of_instances.inspect
puts
puts "Cars: " + Car.array_of_instances.inspect
#--------------------------------------------------------

Problem: “Vehicle.array_of_instances” and “Car.array_of_instances” have
same 3 instantiated objects !

Question: How do I make (this example) “Vehicle.array_of_instances” to
have only two objects with attributes “truck” and “ship” and
“Car.array_of_instances” to have only one object with attribute
“peugeot” ?

On Mon, Jan 27, 2014 at 5:47 PM, Stu P. D’naim [email protected]
wrote:

I’m bad when I try to explain with my words, so I’ll just post code:

#--------------------------------------------------------
class Vehicle
attr_accessor :test

@@array_of_instances = []

You are using a class variable here. That is the source of your
problems. Class variables in my experience cause more trouble than
cure because they are inherited through a class hierarchy and - to
make things worse - this depends on the order of allocation.

Problem: “Vehicle.array_of_instances” and “Car.array_of_instances” have
same 3 instantiated objects !

Question: How do I make (this example) “Vehicle.array_of_instances” to
have only objects with attributes “truck” and “ship” and
“Car.array_of_instances” to have only one object with attribute
“peugeot” ?

You just need to use an instance variable of the class.

#-----------------------------------------
class Vehicle
attr_accessor :test

def self.array_of_instances
@array_of_instances ||= []
end

def initialize(t)
@test = t
self.class.array_of_instances << self
end
end

class Car < Vehicle
end

vehicle1 = Vehicle.new(“truck”)
vehicle2 = Vehicle.new(“ship”)

car1 = Car.new(“Peugeot”)

puts "Vehicles: " + Vehicle.array_of_instances.inspect
puts
puts "Cars: " + Car.array_of_instances.inspect
#-----------------------------------------

On execution:

Vehicles: [#<Vehicle:0x000006002a6920 @test=“truck”>,
#<Vehicle:0x000006002a68a8 @test=“ship”>]

Cars: [#<Car:0x000006002a6858 @test=“Peugeot”>]

Kind regards

robert

Robert K. wrote in post #1134563:
You just need to use an instance variable of the class.

Thanks !
I’ll have to read more about class instance variables

Dave A. wrote in post #1134564:

Off the top of my head… maybe have it be a hash, keyed by the
classes, with the values being the arrays you want?

I have no idea how to append array assigned as value to a hash, my best
guess was something like:
@@hash_of_instances[self.class] = ([] << self) … which of course
doesn’t work …

anyway, from what I googled, seems like its better to avoid usage of
class variables, seems like in Ruby they are something like global
variables with limited scope :frowning:

On Tue, Jan 28, 2014 at 12:58 AM, Stu P. D’naim [email protected]
wrote:

Robert K. wrote in post #1134563:
You just need to use an instance variable of the class.

Thanks !
I’ll have to read more about class instance variables

Essentially they are the same as object instance variables, it’s just
that the object they belong to happens to be a class. Remember that
classes are objects. You just need to be aware of what object is self
at any point and everything just falls into place.

@@hash_of_instances[self.class] = ([] << self) … which of course
doesn’t work …

The best way is to have a default proc for the hash that creates the
array and assigns it to missing keys, then you just append:

hash = Hash.new {|h,k| h[k] = []}

hash[self.class] << self

anyway, from what I googled, seems like its better to avoid usage of
class variables, seems like in Ruby they are something like global
variables with limited scope :frowning:

Indeed, and for your use case go with instance variables as Robert has
suggested.

Jesus.

On Mon, Jan 27, 2014 at 11:47 AM, Stu P. D’naim [email protected]
wrote:

@@array_of_instances = []

Gotcha! Class vars (w/ @@) don’t behave in Ruby the way you’re
probably used to in most other languages. Long story short, the base
class shares with the derived class not only the fact that there IS
such a var… but the var ITSELF. That is, it’s the same
@@array_of_instances whether you access it from Vehicle, Car, Truck,
DieselLocomotive, BicycleBuiltForTwo, or SurreyWithFringeOnTop.

See my “Ruby Gotchas” presentation at:

https://docs.google.com/presentation/d/1cqdp89_kolr4q1YAQaB-6i5GXip8MHyve8MvQ_1r6_s/

In particular, slide #20. In case I add more slides before you see
it, I mean the one titled “Oversharing”.

Question: How do I make (this example) “Vehicle.array_of_instances” to
have only objects with attributes “truck” and “ship” and
“Car.array_of_instances” to have only one object with attribute
“peugeot” ?

Off the top of my head… maybe have it be a hash, keyed by the
classes, with the values being the arrays you want? Or you could find
some different approach to whatever the actual problem is you’re
trying to solve.

-Dave

On Tue, Jan 28, 2014 at 12:15 PM, Stu P. D’naim [email protected]
wrote:

I see in docs you can also do similar behind Array.new, very useful
stuff, thanks again to everyone !

But there it behaves differently: the Array will be created and filled
with values obtained from the block:

Ruby version 1.9.3
irb(main):001:0> a = Array.new(5) {|i| “item %p” % i}
=> [“item 0”, “item 1”, “item 2”, “item 3”, “item 4”]

Cheers

robert

Jesús Gabriel y Galán wrote in post #1134629:

Essentially they are the same as object instance variables, it’s just
that the object they belong to happens to be a class. Remember that
classes are objects. You just need to be aware of what object is self
at any point and everything just falls into place.

Thanks, I wrote this down in my booklet and started using puts
self.inspect everywhere :slight_smile:

The best way is to have a default proc for the hash that creates the
array and assigns it to missing keys, then you just append:

hash = Hash.new {|h,k| h[k] = []}

hash[self.class] << self

This is beautiful ! I didn’t know you can put proc behind Hash.new

From the ruby-doc.org: Hash.new {|hash, key| block } → new_hash

h = Hash.new { |hash, key| hash[key] = “Go Fish: #{key}” }
h[“c”] #=> “Go Fish: c”
h[“c”].upcase! #=> “GO FISH: C”
h[“d”] #=> “Go Fish: d”
h.keys #=> [“c”, “d”]

I see in docs you can also do similar behind Array.new, very useful
stuff, thanks again to everyone !

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs