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 objects with attributes "truck" and "ship" and
"Car.array_of_instances" to have only one object with attribute
"peugeot" ?

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

  @@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:

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 Mon, Jan 27, 2014 at 11:47 AM, Stu P. D'naim <lists@ruby-forum.com> wrote:

--
Dave Aronson, the T. Rex of Codosaurus LLC (codosaur.us),
freelance software developer, and creator of these sites:
PullRequestRoulette.com, blog.codosaur.us, & Dare2XL.com.

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.

···

On Mon, Jan 27, 2014 at 5:47 PM, Stu P. D'naim <lists@ruby-forum.com> wrote:

#-----------------------------------------
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

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Robert Klemme 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 Aronson 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? Or you could find
some different approach to whatever the actual problem is you're
trying to solve.

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:

···

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

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

Dave Aronson 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? Or you could find
some different approach to whatever the actual problem is you're
trying to solve.

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

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 Tue, Jan 28, 2014 at 12:58 AM, Stu P. D'naim <lists@ruby-forum.com> wrote:

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 !

···

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

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

···

On Tue, Jan 28, 2014 at 12:15 PM, Stu P. D'naim <lists@ruby-forum.com> wrote:

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

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/