Instance variables, check. Class variables, check. Class-level instance variables, ..?. Explained in a fruity way.

Recently I was working on a few small systems for demonstration purposes and it brought me back to a little part of Ruby that I hadn't needed to use for a long time now, to be honest had forgotten about until I started thinking about it again, and that is class-level instance variables.

What the block is a class-level instance variable?

Allow me to explain, if I may be so bold.

Starting with the obvious

instance variables are used all the time, and are variables that are created on the instantiation of a class, they are a independant properties of that instance, ie: each instance holds its own value for a given instance variable, without affecting values for seperate instances. These can be declared / created in a variety of ways, one being:

# I just had an apple as part of my lunch
# so we will be using a variety of fruit to show these concepts
class Fruit
  attr_accessor :shape
  # Most fruits are round......I think?
  def initialize(shape='round')
    @shape = shape
  end
end

We can then instantiate as many fruit as we like and freely change their shape independantly of each other.

> apple = Fruit.new
=> #<Fruit:0x007fc7065473b8 @shape="round">
> apple.shape
=> "round"

> orange = Fruit.new
=> #<Fruit:0x007fc7065473b8 @shape="round">
> orange.shape
=> "round"
# The apple had a bit of an accident
> apple.shape = "squashed"
> apple.shape
=> "squashed"
> orange.shape
=> "round"

Nothing suprising there, all went as expected.

Continuing with the obvious

Class variables are variables tied to a certain class, has nothing to do with any independant instantiations, a class-wide value for a class and all its subclasses. So changing the value from one subclass, changes it for all. So doing something like:

class Fruit
  @@shape = 'round'

  def self.shape
    @@shape
  end

  def self.shape=(val)
    @@shape = val
  end
end

class Apple < Fruit
  ...
end

class Orange < Fruit
  ...
end

Would behave like:

> Apple.shape
=> "round"

> Orange.shape
=> "round"

# We squash the apple
> Apple.shape = "squashed"
> Apple.shape
=> "squashed"
> Orange.shape
=> "squashed"
> Fruit.shape
=> "squashed"

Again all as expected, in squashing all apples, we also managed to magically sqaush all oranges, as well as squash all fruit around the world, invoking the wrath of fruit-lovers everywhere.

Behaviour that looks like overriding behaviour also just overwrites the original variable:

class Fruit
  @@shape = 'round'
  ...
end

class Apple < Fruit
end

class Banana < Fruit
  @@shape = '...you know what it looks like, dont make me say it.'
end
> Banana.shape
=> "...you know what it looks like, dont make me say it."
> Apple.shape
=> "...you know what it looks like, dont make me say it."
# whats so rude about an apple?

Ending with the ever-so-slightly-less obvious but still pretty obvious

Class-level instance variables are variables that act as class variables, but can be overridden in sub classes to create an independant class variable that doesn't have any effect on other classes class variables. They are initiated in a similar way to normal instance variables, but at a class level, and a setter / getter can be defined on the class object's metaclass / eigenclass:

class Fruit
  class << self
    attr_accessor :shape
  end
  @shape = '' # No shape exists for default fruit
end

class Apple < Fruit
  @shape = 'round'
end
class Orange < Fruit
  @shape = 'round'
end
class Banana < Fruit
  @shape = 'man sausage'
end

This allows each sublcass of fruit to have and handle its own shape variable:

> apple = Apple.new
> second_apple = Apple.new
> apple.class.shape
=> "round"
> second_apple.class.shape
=> "round"

> orange = Orange.new
> Orange.shape
=> "round"

> banana = Banana.new
> Banana.shape
=> "man sausage"

# Squash all the apples
> Apple.shape = 'squashed'
> Apple.shape
=> "squashed"
> apple.class.shape
=> "squashed"
> second_apple.class.shape
=> "squashed"
> orange.class.shape
=> "round"
> banana.class.shape
=> "man sausage"

And now we are only invoking the wrath of Apple lovers, other fruit lovers are disasater-free.

Conclusion

There isn't so much a conclusion to this blog, it's more of a musing to myself to help reinforce my own knowledge and understanding of the class-level instance variables concept, as it is something I had forgotten about for a little while. Retaining and remembering these basic concepts of any language are always important to strengthen our core abilities as programmers, and I hope this can help anyone else who has managed to let this little concept slip their mind.

But if there is any positive lesson in both programming and in life that can be taken away from this blog, I think that it would be: Don't accidentally squash everyone's fruit. Deep.

SNS

Contact Me

  • john.hayes.reed@gmail.com

Recent Activity

Attended Kansai Ruby Kaigi 2017

A Ruby conference in the kansai region of japan, held in Osaka. Listened to a variety of presentations from many people in the Ruby community. This years theme was Community and Business.

Bottled observers gem release

The first production version of bottled_observers has been released (v0.1.0)


New blog post!

A blog about more advanced decorating concepts in ruby.


Bottled decorators gem update

v0.1.5 of bottled decorators has been released.


New blog post!

A blog post about the concept of class-instance variables


Website Design Update

johnhayesreed.com has had a makeover with Bootstrap 4

New blog post!

A new blog about design patterns in rails - Decorators


Bottled decorators gem release

The first production version of bottled_decorators has been released (v0.1.4)


New blog post!

A new blog about desing patterns in rails - Services


Bottled services gem release

The first production version of bottled_services has been released (v0.1.3)


Ruby Rampage 2016

Took part in 2016's Ruby Rampage 48 hour Hackathon.