Classes

General Syntax:

Ruby is an object-oriented-programming language (OOP), which means it manipulates programming constructs called objects. All objects have methods and attributes. For example: Matz.length #==> 4. In this case, the “Matz” object is a string, with a method of .length, and an attribute of 4. What makes Matz a string though? The fact that it is an instance of the class String. A class is just a way of organizing and producing objects with similar methods and attributes.

Class syntax looks like this:

class ClassName
 # Do something
end

Class Names:

Class names start with an uppercase letter and use camelCase instead of snake_case. In this example, we tell ruby that we want to create a new class, then we put the ClassName to give the class a name, and then we insert whatever code inside that class. ClassName is now a class, just like “Hello!”, is a String (which is a class) and 4 is a Fixnum (which is also a class).

Initialize Method & Creating a Class:

When creating a class, you want to create a method called initialize. When you create a new instance, it will run the initialize method, and execute any code in that method. initialize takes however many arguments you tell it to take. In ruby, we use the @ operator before a variable to signify that it is an instance variable. This means that the variable is attached to the instance of the class. They are unique to each instance.

For example:

class Car
 def initialize(make, model)
  @make = make
  @model = model
 end
 end

time_machine = Car.new(Delorean, DMC-12)
# ==> This is the DeLorean from Back to The Future

In this example, we created an instance time_machine of the class Car. time_machine has its own make of DeLorean and its own model of DMC-12. So these variables only belong to this instance, thus, this makes these variables instance variables.

Creating a New Instance of a Class:

Creating a new instance of a class looks like this: me = Person.new(“Eric”). Where me is a new instance of the class Person, and it takes an argument of “Eric”.

Different Kinds of Variables:

Another important aspect of Ruby classes is scope. The scope of a variable is the context in which it’s visible to the program. Not all variables are accessible to all parts of the program. When dealing with classes, you can have variables that are accessible to all parts of the program (global variables), variables that are only available to certain methods (local variables), and variables that are only available to a particular instance of a class (instance variables). The same goes for methods, some are available everywhere, some can only be accessible to members of that class, and some are only accessible to a particular instance.

Naming Your Variables:

Instance variables start with the @operator. Class variables are like instance variables, but instead of belonging to an instance of a class, they belong to the class itself. Class variables always start with the @@ operator. Global variables can be declared in two ways: The first is just to define the variable outside of any classes or methods, then it is global. If you want to make a variable global from inside a method or class, you start it with the $operator.

An example of a class with different kinds of variables:

class Person
  @@people_count = 0
  def initialize(name, age, profession)
    @name = name
    @age = age
    @profession = profession
    @@people_count += 1
  end

  def self.number_of_instances
    return @@people_count
  end
end

batman = Person.new(“Bruce Wayne, 35,Superhero”)
superman = Person.new(“Clark Kent, 35,Superhero”)
bill_gates = Person.new(“Bill Gates, 45,Software Engineer”)

puts Person.number_of_instances
# ==> It would print 3, because we have batman, superman, and bill_gates

In this example, we create a class variable called @@people_count and set it to 0. Then we add it to the initialize method, and every time an instance is initialized, it adds one to people_count. We define a new method called number_of_instances and have it return the current @@people_count. We then print the number_of_instances method to the console to see how many instances have been created of the People class.

Class Methods:

Just like how there are class variables, and instance variables, the same is true for methods. A class method belongs to the class as a whole, while an instance method belongs to certain instances.

Here is the comparison between the two:

class Person
  @@people = []
  # This is a class variable that is equal to an empty array

  def initialize(name, age, profession)
    @name = name
    @age = age
    @profession = profession
    @@people << name
    # We are pushing the names of the people into the array
  end

  def Person.people_names
    # This is a class method. Syntax: ClassName.method_name
    puts @@people
  end

  # This is an instance method, that only applies to whatever instance we call it on
  def greeting
    putsHello, my name is #{name}, I’m #{age} years old and I work as a #{profession}!”
  end
end

eric = Person.new(“Eric, 25,Doctor”)

# Calling them would look like this:

Person.people_names
# We’re printing all the names of every instance of the class People

Person.greeting
# This would not work because Person doesn’t exist, it’s just a class

eric.greeting
# This would work, because Eric is an actual instance of the class Person

Real-World Classes:

However, in the real world, most classes will not look like the examples above. They will more closely resemble real-world objects.

Here is an example of a class that would most likely be seen in commercial software:

def create_record(attributes, raise_error = false)
  record = build_record(attributes)
  yield(record) if block_given?
  saved = record.save
  set_new_record(record)
  raise RecordInvalid.new(record) if !saved && raise_error
  record
end

Notice how it creates an instance of the RecordInvalid class. The syntax should all be familiar, except the raise bit. All it does is generate a new RecordInvalid error if the user tries to create or save an invalid record.

Inheritance:

Inheritance is the process by which one class takes on the methods and attributes of another, and it’s used to express an is-a relationship. For example, a human is-a mammal, so it could inherit it’s methods and attributes from the mammal class, but a human is not a fox, so it could not inherit its methods and attributes from the fox, despite them both being mammals. The class inheriting from another class is called the sub-class or the derived-class. The class it is inheriting from is called the super-class, the parent, or the base class.

Inheritance syntax works like so:

class SubClass < BaseClass
  # Do something
end

Where SubClass is the new class that is being made, and the BaseClass is the class from which it is inheriting its methods and attributes from. You can read < as "inheriting from".

Overriding Inheritance:

Sometimes you will want one class that inherits from another to not only take on the parent’s methods and attributes, but to also override one or more of them. For example: You might have an Email class that inherits from the Message class. Both classes will have a send method, but sending an email might require different protocols that Message doesn’t have. Instead of creating a new method in your Email class specifically for that, you can just create a method named send and it will override the send method that it inherited from Message. Also, sometimes you will be working inside a sub-class and realize you have overwritten a method or attribute from its super-class that you actually need. No problem! You can directly access a super-class’s methods or attributes by using Ruby’s built in super keyword. When you call the super keyword from inside a sub-class method or attribute, it will tell Ruby to look in the super-class and find a method or attribute with the same name as the one from which super is called. Ruby will then use the super-class method/attribute instead.

Multiple Inheritances:

Any given Ruby class can only have one parent class. Some languages allow a class to have more than one parent, which is called a multiple inheritance. This can get really ugly really fast, which is why Ruby doesn’t allow it. However, there are circumstances where you want to incorporate data or behavior from several classes into one sub-class. Ruby allows this through the use of mix-ins.

For example:

class Creature
  def initialize(powers)
    @powers = powers
  end
end

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

class Dragon < Creature; end
# This is shorthand for creating a class
class Dragon < Human; end
# This is shorthand for creating a class

# ==> Outputs error: "superclass mismatch for class Dragon”

We tried to have Dragon inherit from both Human and Creature, but Ruby gave us an error that basically says “Dragon seems to be inheriting from more than one class, please fix this so that Dragon only has one super-class.”

Public and Private Methods:

Ruby allows you to explicitly make some methods public, and some methods private. public methods allow for an interface with the rest of the program. They say: “Hey! Ask me if you need to know something about my class or its instances!”. private methods on the other hand are for your classes to do their own work undisturbed. They don’t want anyone asking them anything, so they make themselves unreachable. Methods in Ruby are public by default. If you want to make it clear whether your method is public or private, then you have to put either public or private before your method definition.

Like so:

class ClassName
  # Class stuff

  public
  def public_method
    # Code here
  end

  private
  def private_method
    # Code here
  end
end

Note that everything after the public keyword until the end of the class definition will then be public, unless we state otherwise. Private methods are private to the object that they are defined in. This means you can only call the method from inside other methods within that object. Another way to say it is that the method cannot be called using an explicit receiver. Receivers are the objects that methods are called on. For example: object.method, where object is the receiver of the method. In order to access private information, we have to create public methods that know how to get it. This separates the private implementation from the public interface.

Example of Putting it All Together:

class Account
  attr_reader :balance
  attr_reader :name
  def initialize(name, balance=100)
    @name = name
    @balance = balance
  end

  private

  def pin
    @pin = 1234
  end

  def pin_error
    return "Access denied: incorrect PIN."
  end

  public

  def display_balance(pin_number)
    if pin_number == pin
      puts "Balance: #{@balance}"
    else
      puts pin_error
    end
  end

  def withdraw(pin_number, amount)
    if pin_number == @pin
      @blanace -= amount
      puts "Withdrew #{amount}. New balance: #{balance}"
    else
      puts pin_error
    end
  end
end


checking_account = Account.new(“John Doe", 100_000_000)
checking_account.display_balance(1234)

Last updated