Classes and Modules in ruby have always intrigued me!
So, I thought I’ll write some code to understand what they were.
Besides the standard Class aspects of ruby, there are a lot of interesting things that can be done with them – like singleton objects.
Modules, of course, give another level of flexibility.
Class
class variables
class Entity @@instances = 0 def initialize @@instances += 1 @number = @@instances end def who_am_i "I'm #{@number} of #{@@instances}" end def self.total @@instances end def modify_class_variable @@instances = 100 end end
create some entities
entities = Array.new(9) { Entity.new } p entities[6].who_am_i # => "I'm 7 of 9" p Entity.total # => 9
class variables can be modified by their instances
entities[3].modify_class_variable p Entity.total p entities[3].who_am_i # => "I'm 4 of 100"
class instance variables
In the code below, @instances is a class instance variable. It does not belong to an instance of class Entity, but to the class object Entity, which is an instance of class Class.
Class instance variables are directly accessible only within class methods of the class.
class Entity @instances = 0 class << self attr_accessor :instances # provide class methods for reading/writing end def initialize self.class.instances += 1 @number = self.class.instances end def who_am_i "I'm #{@number} of #{self.class.instances}" end def self.total @instances end def modify_class_variable self.class.instances = 100 end end
This gives us the following output
entities = Array.new(9) { Entity.new } p entities[6].who_am_i # => "I'm 7 of 9" p Entity.instances # => 9 p Entity.total # => 9
class instance variables can also be modified by their instances
entities[3].modify_class_variable p Entity.total p entities[3].who_am_i # => "I'm 4 of 100"
class and instance methods
This one is pretty straightforward. Add self. before any method to make it a class method.
class Foo def self.test "this is from Foo class" end def mytest "this is from an instance" end end
This gives us the following output
foo = Foo.new p Foo.test # => "this is from Foo class" p foo.mytest # => "this is from an instance" p foo.test # => private method 'test' called
singleton class
Per the definition –
A singleton class is an anonymous class that is created by subclassing the class associated with a particular object. Singleton classes are another way of extending the functionality associated with just one object.
Here is a standard class and object …
class Moo def hello "hello" end end
and it’s output …
moo = Moo.new p moo.hello # => "hello"
And here’s a singleton class.
class << moo attr_accessor :name def hello "hello, I'm #{name}" end end
and it’s output …
moo.name = "Tom" p moo.hello # => "hello, I'm Tom" p moo.class # => MOo noo = Moo.new p noo.hello # => "hello"
However, this feels more like adding methods directly into objects (which actually it is!)
Note: moo instance has been customized without changing Moo class
singleton object
This is a singleton object, which I find very interesting!
foo = Class.new class << foo attr_accessor :name def hello "hello, I'm #{name}" end end
The output, looks like this!
foo.name = "Tom" p foo.hello # => "hello, I'm Tom" p foo.class # => Class
auto executing object
x = Class.new do def run! p "job done" end end.new.run!
The output, obviously, looks like this!
"job done"
object specific classes
And finally, what if I could have a string which can have it’s own custom methods!
a = "Tom" class <<a def hello "hello I'm #{self}" end end
p a # => "Tom" p a.hello # => "hello, I'm Tom"
Module
Modules in ruby are pretty simple. But then we can do some other stuff with it also.
Examples from Ruby Lang
Module functions
Modules basically can have methods inside them, which can be called directly. Provided the module_function has been set.
module Foo def hello "hello" end # * has to be defined after the method!! module_function :hello end
The output –
p Foo.hello # => "hello"
Module constants
Modules can also have constants defined within them. They are only available within the module.
module Foo # * can be assigned anywhere, either at the top or at the bottom NAME = "Tom" def hello "hello, I'm #{NAME}" end module_function :hello end
The output then, looks like this.
p Foo.hello # => "hello, I'm Tom" p NAME # => uninitialized constant NAME p Foo.NAME # => undefined method `NAME' for Foo:Module
Module mixins using ‘include’
So basically we can club a bunch of methods together into a module and then can include them in any class where I need them.
NOTE: In this case we cannot define the module method as a module function
module Foo def hello "hello, I'm #{NAME}" end NAME = "Tom" end class Moo include Foo def bye "bye from #{NAME}" end end
Output –
moo = Moo.new p moo.hello # => "hello, I'm Tom" p moo.bye # => "bye from Tom"
Module ‘extend’ – extend object functionality
But what if we only want to extend the functionality of only a single object?
module Foo NAME = "Jerry" def hello "hello, I'm #{@name}" end def hellojerry "hello from #{NAME}" end end class Moo def initialize(name) @name = name end def bye "bye from #{@name}" end end
In this case the functionality of the moo object is extended with the module Moo
moo = Moo.new('Tom') moo.extend(Foo) p moo.hello # => "hello, I'm Tom" p moo.bye # => "bye from Tom" p moo.hellojerry # => "hello from Jerry"