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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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
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
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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
entities = Array.new(9) { Entity.new }
p entities[6].who_am_i # => "I'm 7 of 9"
p Entity.total # => 9
entities = Array.new(9) { Entity.new } p entities[6].who_am_i # => "I'm 7 of 9" p Entity.total # => 9
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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
entities[3].modify_class_variable
p Entity.total
p entities[3].who_am_i # => "I'm 4 of 100"
entities[3].modify_class_variable p Entity.total p entities[3].who_am_i # => "I'm 4 of 100"
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.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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
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
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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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
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
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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
entities[3].modify_class_variable
p Entity.total
p entities[3].who_am_i # => "I'm 4 of 100"
entities[3].modify_class_variable p Entity.total p entities[3].who_am_i # => "I'm 4 of 100"
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.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class Foo
def self.test
"this is from Foo class"
end
def mytest
"this is from an instance"
end
end
class Foo def self.test "this is from Foo class" end def mytest "this is from an instance" end end
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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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
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
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 …

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class Moo
def hello
"hello"
end
end
class Moo def hello "hello" end end
class Moo
  def hello
    "hello"
  end
end

and it’s output …

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
moo = Moo.new
p moo.hello # => "hello"
moo = Moo.new p moo.hello # => "hello"
moo = Moo.new
p moo.hello     # => "hello"

And here’s a singleton class.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class << moo
attr_accessor :name
def hello
"hello, I'm #{name}"
end
end
class << moo attr_accessor :name def hello "hello, I'm #{name}" end end
class << moo
  attr_accessor :name

  def hello
    "hello, I'm #{name}"
  end
end

and it’s output …

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
moo.name = "Tom"
p moo.hello # => "hello, I'm Tom"
p moo.class # => MOo
noo = Moo.new
p noo.hello # => "hello"
moo.name = "Tom" p moo.hello # => "hello, I'm Tom" p moo.class # => MOo noo = Moo.new p noo.hello # => "hello"
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!

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
foo = Class.new
class << foo
attr_accessor :name
def hello
"hello, I'm #{name}"
end
end
foo = Class.new class << foo attr_accessor :name def hello "hello, I'm #{name}" end end
foo = Class.new
class << foo
  attr_accessor :name

  def hello
    "hello, I'm #{name}"
  end
end

The output, looks like this!

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
foo.name = "Tom"
p foo.hello # => "hello, I'm Tom"
p foo.class # => Class
foo.name = "Tom" p foo.hello # => "hello, I'm Tom" p foo.class # => Class
foo.name = "Tom"
p foo.hello  # => "hello, I'm Tom"
p foo.class  # => Class

auto executing object


Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
x = Class.new do
def run!
p "job done"
end
end.new.run!
x = Class.new do def run! p "job done" end end.new.run!
x = Class.new do
  def run!
    p "job done"
  end
end.new.run!

The output, obviously, looks like this!

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
"job done"
"job done"
"job done"

object specific classes


And finally, what if I could have a string which can have it’s own custom methods!

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
a = "Tom"
class <<a
def hello
"hello I'm #{self}"
end
end
a = "Tom" class <<a def hello "hello I'm #{self}" end end
a = "Tom"
class <<a
  def hello
    "hello I'm #{self}"
  end
end
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
p a # => "Tom"
p a.hello # => "hello, I'm Tom"
p a # => "Tom" p a.hello # => "hello, I'm Tom"
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.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
module Foo
def hello
"hello"
end
# * has to be defined after the method!!
module_function :hello
end
module Foo def hello "hello" end # * has to be defined after the method!! module_function :hello end
module Foo
  def hello
    "hello"
  end

  # * has to be defined after the method!!
  module_function :hello
end

The output –

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
p Foo.hello # => "hello"
p Foo.hello # => "hello"
p Foo.hello  # => "hello"

Module constants

Modules can also have constants defined within them. They are only available within the module.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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
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
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.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
p Foo.hello # => "hello, I'm Tom"
p NAME # => uninitialized constant NAME
p Foo.NAME # => undefined method `NAME' for Foo:Module
p Foo.hello # => "hello, I'm Tom" p NAME # => uninitialized constant NAME p Foo.NAME # => undefined method `NAME' for Foo:Module
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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
module Foo
def hello
"hello, I'm #{NAME}"
end
NAME = "Tom"
end
class Moo
include Foo
def bye
"bye from #{NAME}"
end
end
module Foo def hello "hello, I'm #{NAME}" end NAME = "Tom" end class Moo include Foo def bye "bye from #{NAME}" end end
module Foo
  def hello
    "hello, I'm #{NAME}"
  end

  NAME = "Tom"
end

class Moo
  include Foo

  def bye
    "bye from #{NAME}"
  end
end

Output –

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
moo = Moo.new
p moo.hello # => "hello, I'm Tom"
p moo.bye # => "bye from Tom"
moo = Moo.new p moo.hello # => "hello, I'm Tom" p moo.bye # => "bye from Tom"
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?

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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
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
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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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"
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"
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"