OdinSchool OdinSchool
Ruby Class | Learning Hub

Ruby Class | Learning Hub

Each class in ruby starts with the keyword Class followed by the name of the class. Each class in ruby is an instance of the class Class. 

Creating a class

Each class in Ruby can be defined using the keyword class followed by the name of the class. 

We can create a new instance of a class using the new method as follows

You can define a new class using theclasskeyword.

class MyClass
end

Once defined, you can create a new instance using the.newmethod

somevar = MyClass.new
# => #<MyClass:0x007fe2b8aa4a18>

Constructor

Each class has a single method known as a constructor. A constructor is a method that is invoked automatically when a new instance of the class is created

In ruby the constructor method in a class is defined using the method initialize .

class Customer def initialize(name) @name = name.capitalize end end

sarah = Customer.new('sarah')
sarah.name #=> 'Sarah'

Access Levels

Every class in Ruby has three access levels
public, private and protected

Public Methods

The methods declared in a public class can be invoked and utilized anywhere outside the class by creating a new instance of the class.

class Cat
def initialize(name)
@name = name
end

def speak
puts "I'm #{@name} and I'm 2 years old"
end

...
end

new_cat = Cat.new("garfield")
#=> <Cat:0x2321868 @name="garfield">

new_cat.speak
#=> I'm garfield and I'm 2 years old

These methods are public ruby methods, they describe the behavior for initializing a new cat and the behavior of the speak method.

publickeyword is unnecessary, but can be used to escapeprivateorprotected

def MyClass
def first_public_method
end

private

def private_method
end

public

def second_public_method
end
end

Private Methods

Private methods are not accessible outside the class they are declared in. They are used internally by the class.

class Cat
def initialize(name)
@name = name
end

def speak
age = calculate_cat_age # here we call the private method
puts "I'm #{@name} and I'm #{age} years old"
end

private
def calculate_cat_age
2 * 3 - 4
end
end

my_cat = Cat.new("Bilbo")
my_cat.speak #=> I'm Bilbo and I'm 2 years old
my_cat.calculate_cat_age #=> NoMethodError: private method `calculate_cat_age' called for #<Cat:0x2321868 @name="Bilbo">

As you can see in the example above, the newly created Cat object has access to thecalculate_cat_agemethod internally. We assign the variableageto the result of running the privatecalculate_cat_agemethod which prints the name and age of the cat to the console.

When we try and call thecalculate_cat_agemethod from outside themy_catobject, we receive aNoMethodErrorbecause it's private. Get it?

Protected Methods

Protected methods can be called by objects of the same type by using the self keyword.

class Cat
def initialize(name, age)
@name = name
@age = age
end

def speak
puts "I'm #{@name} and I'm #{@age} years old"
end

# this == method allows us to compare two objects own ages.
# if both Cat's have the same age they will be considered equal.
def ==(other)
self.own_age == other.own_age
end

protected
def own_age
self.age
end
end

cat1 = Cat.new("ricky", 2)
=> #<Cat:0x007fe2b8aa4a18 @name="ricky", @age=2>

cat2 = Cat.new("lucy", 4)
=> #<Cat:0x008gfb7aa6v67 @name="lucy", @age=4>

cat3 = Cat.new("felix", 2)
=> #<Cat:0x009frbaa8V76 @name="felix", @age=2>

You can see we've added an age parameter to the cat class and created three new cat objects with the name and age. We are going to call theown_ageprotected method to compare the age's of our cat objects.

cat1 == cat2
=> false

cat1 == cat3
=> true

Look at that, we were able to retrieve cat1's age using theself.own_ageprotected method and compare it against cat2's age by callingcat2.own_ageinside of cat1.

Accessing instance variables with getters and setters

We have three methods:

  1. attr_reader: used to allow reading the variable outside the class.
  2. attr_writer: used to allow modifying the variable outside the class.
  3. attr_accessor: combines both methods.
class Cat
attr_reader :age # you can read the age but you can never change it
attr_writer :name # you can change name but you are not allowed to read
attr_accessor :breed # you can both change the breed and read it

def initialize(name, breed)
@name = name
@breed = breed
@age = 2
end
def speak
puts "I'm #{@name} and I am a #{@breed} cat"
end
end

my_cat = Cat.new("Banjo", "birman")
# reading values:

my_cat.age #=> 2
my_cat.breed #=> "birman"
my_cat.name #=> Error

# changing values

my_cat.age = 3 #=> Error
my_cat.breed = "sphynx"
my_cat.name = "Bilbo"

my_cat.speak #=> I'm Bilbo and I am a sphynx cat

Note that the parameters are symbols. this works by creating a method.

class Cat
attr_accessor :breed
end

Is basically the same as:

class Cat
def breed
@breed
end
def breed= value
@breed = value
end
end

Class Methods types

Classes have 3 types of methods: instance, singleton and class methods.

Instance Methods

These are methods that can be called from aninstanceof the class.

class Thing
def somemethod
puts "something"
end
end

foo = Thing.new # create an instance of the class
foo.somemethod # => something

Class Method

These are static methods, i.e, they can be invoked on the class, and not on an instantiation of that class.

class Thing
def Thing.hello(name)
puts "Hello, #{name}!"
end
end

It is equivalent to useselfin place of the class name. The following code is equivalent to the code above:

class Thing
def self.hello(name)
puts "Hello, #{name}!"
end
end

Invoke the method by writing

Thing.hello("John Doe")  # prints: "Hello, John Doe!"

Singleton Methods

Singleton methods are available only to a few instances of the class.

These methods are also called eigenclasses.

# create an empty class
class Thing
end

# two instances of the class
thing1 = Thing.new
thing2 = Thing.new

# create a singleton method
def thing1.makestuff
puts "I belong to thing one"
end

thing1.makestuff # => prints: I belong to thing one
thing2.makestuff # NoMethodError: undefined method `makestuff' for #<Thing>

Both thesingletonandclassmethods are calledeigenclasses. Basically, what ruby does is to create an anonymous class that holds such methods so that it won't interfere with the instances that are created.

Another way of doing this is by theclass << constructor. For example:

# a class method (same as the above example)
class Thing
class << self # the anonymous class
def hello(name)
puts "Hello, #{name}!"
end
end
end

Thing.hello("sarah") # => Hello, sarah!

# singleton method

class Thing
end

thing1 = Thing.new

class << thing1
def makestuff
puts "I belong to thing one"
end
end

thing1.makestuff # => prints: "I belong to thing one"

Dynamic class creation

Classes can be created dynamically through the use ofClass.new.

# create a new class dynamically
MyClass = Class.new

# instantiate an object of type MyClass
my_class = MyClass.new

In the above example, a new class is created and assigned to the constantMyClass. This class can be instantiated and used just like any other class.

TheClass.newmethod accepts aClasswhich will become the superclass of the dynamically created class.

# dynamically create a class that subclasses another
Staffy = Class.new(Dog)

# instantiate an object of type Staffy
lucky = Staffy.new
lucky.is_a?(Staffy) # true
lucky.is_a?(Dog) # true

TheClass.newmethod also accepts a block. The context of the block is the newly created class. This allows methods to be defined.

Duck = 
Class.new do
def quack
'Quack!!'
end
end

# instantiate an object of type Duck
duck = Duck.new
duck.quack # 'Quack!!'

New, allocate, and initialize

In many languages, new instances of a class are created using a specialnewkeyword. In Ruby,newis also used to create instances of a class, but it isn't a keyword; instead, it's a static/class method, no different from any other static/class method. The definition is roughly this:

class MyClass
def self.new(*args)
obj = allocate
obj.initialize(*args) # oversimplied; initialize is actually private
obj
end
end

allocateperforms the real 'magic' of creating an uninitialized instance of the class

Note also that the return value ofinitializeis discarded, and obj is returned instead. This makes it immediately clear why you can code your initialize method without worrying about returningselfat the end.

The 'normal' new method that all classes get fromClassworks as above, but it's possible to redefine it however you like, or to define alternatives that work differently. For example:

class MyClass
def self.extraNew(*args)
obj = allocate
obj.pre_initialize(:foo)
obj.initialize(*args)
obj.post_initialize(:bar)
obj
end
end

Class and instance variables

There are several special variable types that a class can use for more easily sharing data.

Instance variables, preceded by@. They are useful if you want to use the same variable in different methods.

class Person
def initialize(name, age)
my_age = age # local variable, will be destroyed at end of constructor
@name = name # instance variable, is only destroyed when the object is
end

def some_method
puts "My name is #{@name}." # we can use @name with no problem
end

def another_method
puts "My age is #{my_age}." # this will not work!
end
end

mhmd = Person.new("Mark", 23)

mhmd.some_method #=> My name is Mark.
mhmd.another_method #=> throws an error

Class variable, preceded by@@. They contain the same values across all instances of a class.

class Person
@@persons_created = 0 # class variable, available to all objects of this class
def initialize(name)
@name = name

# modification of class variable persists across all objects of this class
@@persons_created += 1
end

def how_many_persons
puts "persons created so far: #{@@persons_created}"
end
end

mark = Person.new("Mark")
mark.how_many_persons #=> persons created so far: 1
helen = Person.new("Helen")

mark.how_many_persons #=> persons created so far: 2
helen.how_many_persons #=> persons created so far: 2
# you could either ask mark or helen

Global Variables, preceded by$. These are available anywhere to the program, so make sure to use them wisely.

$total_animals = 0

class Cat
def initialize
$total_animals += 1
end
end

class Dog
def initialize
$total_animals += 1
end
end

bob = Cat.new()
puts $total_animals #=> 1
fred = Dog.new()
puts $total_animals #=> 2