Ruby Class Instance Variables and Inheritance

Inherit class-level instance variables in Ruby?

Use a mixin:

module ClassLevelInheritableAttributes
def self.included(base)
base.extend(ClassMethods)
end

module ClassMethods
def inheritable_attributes(*args)
@inheritable_attributes ||= [:inheritable_attributes]
@inheritable_attributes += args
args.each do |arg|
class_eval %(
class << self; attr_accessor :#{arg} end
)
end
@inheritable_attributes
end

def inherited(subclass)
@inheritable_attributes.each do |inheritable_attribute|
instance_var = "@#{inheritable_attribute}"
subclass.instance_variable_set(instance_var, instance_variable_get(instance_var))
end
end
end
end

Including this module in a class, gives it two class methods: inheritable_attributes and inherited.

The inherited class method works the same as the self.included method in the module shown. Whenever a class that includes this module gets subclassed, it sets a class level instance variable for each of declared class level inheritable instance variables (@inheritable_attributes).

Ruby—subclasses using instance variables of the parent

One quick way to achieve your goal would be to use the little known SimpleDelegator class:

class A
attr_reader :var

def initialize(var)
@var = var
end
end

require 'delegate'

class B < SimpleDelegator
def test
puts var
end
end

a1 = A.new('one')
a2 = A.new('two')
b1 = B.new(a1)
b1 = B.new(a1)
b2 = B.new(a2)
b3 = B.new(a2)

b3.test
#=> 'two'
b2.test
#=> 'two'
b1.test
#=> 'one'

This blog article ("5 ways of forwarding your work to some other object in Ruby") might be of interest to you.

Inheritance and instance variable in Ruby

B inherits initialize from A.

At object creation, initialize is invoked. So you get @x set to 2 even for objects of class B.

I think, the sentences you are quoting refer to this scenario:

class A
def initialize
@x = 42
end
end

class B < A
def initialize
@x = 23
end
end

h = B.new

Now, h has just one instance variable @x with value 23. It is not like there is one @x from B and one from A. You can see this here:

class A
def initialize
@x = 42
end

def set_x_from_a
@x = 12
end

def print_x_from_a
puts @x
end
end

class B < A
def initialize
@x = 23
end

def set_x_from_b
@x = 9
end

def print_x_from_b
puts @x
end
end

h = B.new
h.print_x_from_a # => 23
h.print_x_from_b # => 23
h.set_x_from_a
h.print_x_from_b # => 12
h.set_x_from_b
h.print_x_from_a # => 9

Ruby class instance variables and inheritance

Since you mention that the attributes are "fixed" and "unchanging", I am assuming that you mean that you will never change their value once the object is created. In that case, something like the following should work:

class Foo
ATTRS = ['title', 'authors', 'location']
def attributes
ATTRS
end
end

class Bar < Foo
ATTRS = ['ISBN', 'pages']
def attributes
super + ATTRS
end
end

You are manually implementing a reader method (instead of letting attr_accessor create it for you) that disguises the internal name of the array. In your subclass, you simply call the ancestor class' reader function, tack on the additional fields associated with the child class, and return that to the caller. To the user, this appears like a read-only member variable named attributes that has additional values in the sub-class.

Instance Variables Inheritance

You are right, the book is wrong, or at least poorly worded


I would argue that the book is simply wrong, or at best, it's making a quite muddy explanation.

In all OO languages, the superclass and derived class don't have separate objects. When you create an instance of the derived class, it is also an instance of the superclass. There is one object and it is both classes at once.

Since there is only one object, there is only one set of instance variables.

This is the same as all other OO systems. The weird argument that book makes about how it just matters which method is run and how the methods themselves are what are really inherited does not add much in the way of clarity.

The problem with the terminology is that, sure, in a dynamically typed system there is no declaration in the first place, and so certainly the definition of the subclass doesn't inherit any field declarations ... because of course there aren't any. But just because there are no types to inherit doesn't make the opposite statement ("instance variables are not inherited") any more true, and it adds quite a bit of confusion because it implies that somehow the parent would have different instance variables, which is the nonsensical result of trying to talk about the objects the way they do.

How to declare Class Instance Variable in inherited class automatically

I don't know if this is the best way, but here is how I might do it:

class Entity
singleton_class.send(:attr_writer, :instance_counter)
def self.instance_counter; @instance_counter ||= 0; end
def initialize
self.class.instance_counter += 1
end
end

Initial value for class instance variable in inheritance chain

While the suggestion provided by @Stefan in comments is probably the way to go, it’s still possible with metaprogramming; one just needs to handle inheritance.

module Foo
def self.included(base)
base.class_eval { @foos = [] }
class << base
attr_reader :foos
def inherited(other)
Foo.included(other)
end
end
end
end

class Bar
include Foo
end

class Baz < Bar; end

Bar.foos #⇒ []
Baz.foos #⇒ []

Ruby class instance variable vs. class variable

Instance variable on a class:

class Parent
@things = []
def self.things
@things
end
def things
self.class.things
end
end

class Child < Parent
@things = []
end

Parent.things << :car
Child.things << :doll
mom = Parent.new
dad = Parent.new

p Parent.things #=> [:car]
p Child.things #=> [:doll]
p mom.things #=> [:car]
p dad.things #=> [:car]

Class variable:

class Parent
@@things = []
def self.things
@@things
end
def things
@@things
end
end

class Child < Parent
end

Parent.things << :car
Child.things << :doll

p Parent.things #=> [:car,:doll]
p Child.things #=> [:car,:doll]

mom = Parent.new
dad = Parent.new
son1 = Child.new
son2 = Child.new
daughter = Child.new

[ mom, dad, son1, son2, daughter ].each{ |person| p person.things }
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]

With an instance variable on a class (not on an instance of that class) you can store something common to that class without having sub-classes automatically also get them (and vice-versa). With class variables, you have the convenience of not having to write self.class from an instance object, and (when desirable) you also get automatic sharing throughout the class hierarchy.


Merging these together into a single example that also covers instance variables on instances:

class Parent
@@family_things = [] # Shared between class and subclasses
@shared_things = [] # Specific to this class

def self.family_things
@@family_things
end
def self.shared_things
@shared_things
end

attr_accessor :my_things
def initialize
@my_things = [] # Just for me
end
def family_things
self.class.family_things
end
def shared_things
self.class.shared_things
end
end

class Child < Parent
@shared_things = []
end

And then in action:

mama = Parent.new
papa = Parent.new
joey = Child.new
suzy = Child.new

Parent.family_things << :house
papa.family_things << :vacuum
mama.shared_things << :car
papa.shared_things << :blender
papa.my_things << :quadcopter
joey.my_things << :bike
suzy.my_things << :doll
joey.shared_things << :puzzle
suzy.shared_things << :blocks

p Parent.family_things #=> [:house, :vacuum]
p Child.family_things #=> [:house, :vacuum]
p papa.family_things #=> [:house, :vacuum]
p mama.family_things #=> [:house, :vacuum]
p joey.family_things #=> [:house, :vacuum]
p suzy.family_things #=> [:house, :vacuum]

p Parent.shared_things #=> [:car, :blender]
p papa.shared_things #=> [:car, :blender]
p mama.shared_things #=> [:car, :blender]
p Child.shared_things #=> [:puzzle, :blocks]
p joey.shared_things #=> [:puzzle, :blocks]
p suzy.shared_things #=> [:puzzle, :blocks]

p papa.my_things #=> [:quadcopter]
p mama.my_things #=> []
p joey.my_things #=> [:bike]
p suzy.my_things #=> [:doll]

Ruby and class variables in inherit class

Here is a nice article on the subject - Class and Instance Variables In Ruby.

Basically, what you can do is:

class A
class << self
attr_accessor :class_var
end

def set_class_var(value)
self.class.class_var = value
end

def get_class_var
self.class.class_var
end
end

class B < A; end

A.class_var = 'a'
B.class_var = 'b'
puts A.class_var # => a
puts B.class_var # => b

A.new.set_class_var 'aa'
B.new.set_class_var 'bb'
puts A.new.get_class_var # => aa
puts B.new.get_class_var # => bb

To understand it you should think about A as an instance of Class class (and that's how it is in Ruby). But every object in Ruby has its own singleton class that stores object-specific stuff like methods defined on object itself:

a = A.new
def a.foo
puts 'foo'
end

In that case foo is method defined only for a object and not for every instance of A class. And another way to define method in object's singleton class is like that:

class << a # open a's singleton class
def bar # define method that will be available only on 'a' object
puts 'bar'
end
end

In the first code snippet we use that approach to define class_var attribute accessor in the context of singleton class of our A class (it's a bit tricky, so you need to think about it). As the result class itself has class_var variable as well as its descendant class B. The difference is that every one of them has its own class_var variable that do not interfere.

Create a new instance of parent's class variable for each child class

As @CarySwoveland comments, in your use case, you should use a class instance variable:

class ListBase
@list = []

def self.list
@list
end
end

Not even sure why you thought of using a class variable for your use case.



Related Topics



Leave a reply



Submit