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