Creating Classes, Objects, and Variables in Ruby.

So my task today was to focus on establishing all the required basics in constructing classes and instantiating objects from those classes and seeing what I can do with these objects ruby style.

1
2
3
4
5
6
class Animal
  def initialize(name, nickname, sound)
    @name = name
    @nickname = nickname
  end
end

That is all it takes to create a class in ruby, every class name needs to start with an uppercase and the method names start with lowercase, the initialize method is a special method in ruby language, it allows us to set

the class object state by accepting variables.

1
2
  @name = name
  @nickname = nickname

Instance variables in ruby are written starting with "@" sign, and stored within our objects, by default ruby’s inspect message which can be sent to any object, dumps out the object’s id and instance variables.

Inheritance

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Mammal
   def skin
	puts 'Has hair or fur'
   end
 
   def breath
	'Has lungs to breath air'
   end
end
 
class Cat < Mammal
   def sound
	puts 'meow'
   end
end
 
felix = Cat.new
puts felix.skin # prints Has hair or fur
puts felix.sound #prints meow
puts felix.breath #prints Has lungs to breath air

The sign "<" says that Cat inherits from class Animal, if a class does not have a superclass it inherits from, ruby automatically inherits from object class. Line 17 creates a new felix object, and this process is known as instantiation now we have an object which is an instance of the Cat class. The fact that Cat inherits from Mammal makes it possible to call methods in the Mammal class such as breath

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class School
  def to_s(school)
     @school = school
  end
end
 
class Student < School
  def to_s(name)
    puts "#{name} attended #{super('Corpus Christi College')}"
  end
end
 
student1 = Student.new
student1.to_s("Mark")

In this example Student and School classes have the same method "to_s" I then used the "super" keyword, which simply looks in the parent class for a matching method, supplies the argument provided and returns the last line in the parent function. It then adds this to the result which is prints out. To print a variable within double quotes in ruby is achieved by using #{varible} syntax.

Objects and Attributes

1
2
3
4
5
6
7
8
class School
  def initialize
     @name = 'Magdalene College'
  end
end
 
school = School.new
puts school.name

Trying to access the School’s instance variable "name" would result in an error message: "undefined method name" this is because in ruby you don’t have a direct access to attributes unless they are listed in special attribute methods.

1
2
3
4
5
6
7
8
9
10
class School
  attr_reader :name, :teachers
  def initialize
     @name = 'Magdalene College'
     @teachers = '40'
  end
end
 
school = School.new
puts school.name

Now running "puts" on this will produce desired result, this is down to the fact that an attr_reader method was constructed on line 2, the :name is an attribute method, and holds @name variable symbol, making it possible for line 10 to produce the value of @name from line 2, attr_reader is a getter, it is only used to get symbol values, in order to change the value of @name we need a setter attr_writer

1
2
3
4
5
6
7
8
9
10
11
12
class School
  attr_reader :name
  attr_writer :name
  def initialize
     @name = 'Magdalene College'
  end
end
 
school = School.new
puts school.name
school.name = "Corpus Christi College"
puts school.name

The new attribute attr_writer makes it possible to change the state of instance variables, without it, system throws an error. line 11 changed the school name to “Corpus Christi College” this is printed off on line 12.

1
   attr_accessor :name

Using attr_accessor makes it possible to write and read from instance variables without having to specify a write attribute and then a read attriute, using attr_accessor you can kill 2 birds with one stone.

1
2
3
4
5
6
7
8
class School
  def name
     @name = 'Magdalene College'
  end
end
 
school = School.new
puts school.name

A way to get the values of your instance variables without defining any getters and setters attributes, would be to define a method which returns an instance variable, calling that objects method will in turn return its variable, this is not very efficient, as you will have to creates lots of methods with the purpose of only returning instance variables.

Virtual Attributes

1
2
3
4
5
6
7
8
9
10
11
12
13
class Chef
   attr_accessor :food, :name
 
   def chef=(name) 
	@food = "#{name} cooks: jerk chicken"
   end
end
 
obj = Chef.new
obj.food = 'jollof rice and isi ewu'
puts obj.food #jollof rice and isi ewu
obj.chef = 'Kingsley'
puts obj.food #Kingsley cooks: jerk chicken

Virtual variables in ruby are methods we can use as though they were instance variable, they do more than just hold values, in this case chef=(name) allowed me to call it as though it were merely a variable obj.chef = 'kingsley', this then updated the @food variable.

In his landmark book Object-Oriented Software Construction , Bertrand Meyer calls this the Uniform Access Principle. By hiding the difference between instance variables and calculated values, you are shielding the rest of the world from the implementation of your class. You’re free to change how things work in the future without impacting the millions of lines of code that use your class. This is a big win.

Class Variables and Class Methods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Car
   @@sound = 0
   def horn
	@@sound += 1
   end
end
 
car1 = Car.new
puts car1.horn # prints 1
 
car2 = Car.new
puts car2.horn # prints 2
 
car3 = Car.new
puts car3.horn # prints 3

A class variable is shared among all objects of a class, it is written in ruby with @@ appended in front of the variable name. In the example above, I was able to reference the same class variable @@sound from car1 object, car2 object and car3 object, every time a car object calls its own horn method, it remembers the previous objects count and increments it.

1
2
3
4
5
6
7
class Example
   def instanceMethod
   end
 
   def Example.classMethod
   end
end

Above are the examples of instance method and class method in ruby, class method are not tied to any objects in particular. Class methods are written with the class name appended to the beginning of the method followed with a period Example.classMethodName.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Bonus
   attr_accessor :bonus
 
   @@bonus = 0 #percent
 
   def Bonus.apply(object)
	object.balance += (object.balance * @@bonus/100)
   end
 
   def Bonus.rate=(percentage)
	@@bonus = percentage
   end
end
 
class Bank
   attr_accessor :balance
 
   def initialize(balance)
	@balance = balance
   end
end
 
bank = Bank.new(100)
Bonus.rate = 25
puts Bonus.apply(bank) # prints 125

In the example above, I created a bonus class, which simply sets a bonus rate in percentage and then accepts any class object through its class method Bonus.apply it then applies the bonus rate, without the need to instantiate the bonus class we can get the new bonus result thanks to class method.

I also found it was possible to create a class method that is also a virtual variable Bonus.rate=(percentage) this is how I was able to set the bonus rate, again this was possible without first creating a bonus object.

The combination of class method, class variable and class virtual variable helped me achieve this example.

Singletons and Other Constructors

The singleton pattern allows you to make sure that only one object instance exits for a particular class.

1
2
3
4
5
class Logger
   private_class_method :new
end
 
Logger.new #private method `new' called for Logger:Class

Ruby allowed me to make the new method of this class private, when I then tried to create a new instance of this class I got an error: Logger.new #private method `new' called for Logger:Class the next thing is to create a method that would be responsible for creating just one instance of the Logger class.

1
2
3
4
5
6
7
8
9
10
11
12
13
class Logger
   private_class_method :new
 
   @@instance = nil
 
   def Logger.instance
	@@instance = new unless @@instance
	@@instance
   end
end
 
puts Logger.instance.object_id #70241803692620
puts Logger.instance.object_id #70241803692620

In above I created a class method Logger.instance this allows me to access this class without the need to instantiate it first, which I can’t since the new method is private.

Line 7 creates a new object of the Logger class only if it is not already created and stored in the @@instance class variable. Line 8 simply returns the current object.

Line 12 and 13 prints the object id’s, in ruby every object has an id, so as I expected this produced the same object id, which indicates that the Logger.instance generated only one object and then the second time only returned the already created object.

1
2
3
4
5
6
7
8
require 'singleton'
 
class Logger
   include Singleton
end
 
puts Logger.instance.object_id #70239324828480
puts Logger.instance.object_id #70239324828480

Ruby Standard Library has Singleton module which implements Singleton pattern, so the code above achieves my previous example, in this I require and include the Singleton module. Ruby Singleton module does a lazy instantiation (creates the Logger instance at the moment when we call Logger.instance method and not during load time like in the previous example. Ruby Singleton also makes new method private so I didn’t need to call private_class_method. It also creates a class method automatically for us called Logger.instance

Access Control

Public methods: can be called by anyone, there are no access controls, all methods are public by default, except for initialize method which is always private.

Protected methods: can be called within the class it was defined as well as from all subclasses, access is kept within the family.

Private methods: can only be called explicitly from the within the defining class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MyClass
   def method1 # default is public
     #...
   end
 
  protected
   def method2 # will be protected 
     #...
   end
 
  private
   def method3 # will be private
     #...
   end
 
  public
   def method4 # will be public
     #...
   end
end

Alternatively it is also possible to list methods as argument to the access control methods as below:

1
2
3
4
5
6
7
8
9
   def method1
     #...
   end
 
   # ...... other methods
 
   public :method1, :method2
   protected :method3
   private :method4

Variables

In ruby variables are used to keep track of objects, each variable holds a reference to an object.

1
2
  person = "kingsley"
  puts person.object_id

In the example above, the variable person holds a reference to a string object, so we can still do something like person.object_id to give us the object id of the referenced object.

The person variable is not an object, it simply holds a reference to an object.

Tags: , , ,