RUBY · STEP 2 · OOP

Ruby · OOP

Ruby 4.0 (any 3.2+ works): classes, `initialize`, instance methods, inheritance with `super`, and class-level methods and state — the JavaScript version next to the Ruby version.

Step 2 of 5Rails© Arthur Bernier Jrrewritten · verified9 code blocks415 words

What you will learn#

  • How to Create a Class
  • How to Instantiate a Class
  • Write a Constructor and Methods
  • Create an Inherited Class
  • Write Static Methods/Properties

Below you'll see JavaScript examples of object-oriented patterns and their Ruby counterparts.

Creating and Instantiating a Class#

In Javascript

JS
// Creating a Classclass Dog {} // Instantiationconst Sparky = new Dog();

In Ruby

RB
# Creating a class: `class` ... `end`, nothing else requiredclass Dogend # Instantiation: no `new` keyword in front, `.new` is a method you call on the classsparky = Dog.new

Class names are CamelCase and must start with a capital (a capitalised name is a constant in Ruby); variables, methods and files are snake_case.

The Constructor and Methods#

In Javascript

JS
// Creating a Classclass Dog {  // The constructor runs when we instantiate a new instance  constructor(name, age) {    this.name = name;    this.age = age;  }   // A Method  bark() {    console.log(`${this.name} barks`);  }} // Instantiationconst Sparky = new Dog("Sparky", 4);Sparky.bark();

in Ruby

RB
# Creating a classclass Dog  # initialize runs when we call Dog.new; its arguments are whatever you passed to new  def initialize(name, age)    # @name is an instance variable: one per object, visible to every method in the class    @name = name    @age = age  end   # A method  def bark    puts "#{@name} barks"  endend # Instantiationsparky = Dog.new("Sparky", 4)sparky.bark

Where JavaScript has this.name, Ruby has @name. The big difference: instance variables are private. sparky.name is a NoMethodError until you write a reader method, and the shorthand for that is attr_reader (or attr_accessor for read and write):

RB
class Dog  attr_reader :name       # defines a `name` method that returns @name  attr_accessor :age      # defines `age` and `age=`  # ... initialize as aboveend sparky = Dog.new("Sparky", 4)puts sparky.name   # Sparkysparky.age = 5     # calls the age= methodp sparky           # #<Dog:0x... @name="Sparky", @age=5>

p on an object shows its class and instance variables; define inspect to change that, and to_s to control what puts prints.

Inheritance#

In Javascript

JS
// Creating a Classclass Dog {  // The constructor runs when we instantiate a new instance  constructor(name, age) {    this.name = name;    this.age = age;  }   // A Method  bark() {    console.log(`${this.name} barks`);  }} class SmallDog extends Dog {  constructor(name, age) {    //Super calls the constructor of the parent class    super(name, age);  }   //This will override the version of bark in the parent class  bark() {    console.log(`${this.name} yaps`);  }} // Instantiationconst Sparky = new SmallDog("Sparky", 4);Sparky.bark();

in Ruby

RB
# Creating a classclass Dog  def initialize(name, age)    @name = name    @age = age  end   def bark    puts "#{@name} barks"  endend # `<` is Ruby's `extends`class SmallDog < Dog  def initialize(name, age)    # super calls the same method on the parent class, here Dog#initialize    super(name, age)  end   # This overrides the parent's bark  def bark    puts "#{@name} yaps"  endend # Instantiationsparky = SmallDog.new("Sparky", 4)sparky.barkputs sparky.is_a?(Dog)   # true

If a subclass doesn't define initialize, the parent's runs automatically — SmallDog's constructor above could be deleted with no change in behaviour. A bare super (no parentheses) forwards the exact arguments the method received. Ruby classes have one parent only; behaviour shared across unrelated classes goes in a module that you include (a mix-in).

Static Methods and Properties#

In Javascript

JS
// Static properties/methods belong to the class, not the instanceclass Calculator {  static lastResult = 0;   // a table of operator functions instead of eval()  static operations = {    "+": (a, b) => a + b,    "-": (a, b) => a - b,    "*": (a, b) => a * b,    "/": (a, b) => a / b,  };   static calculate(num1, num2, operator) {    Calculator.lastResult = Calculator.operations[operator](num1, num2);    return Calculator.lastResult;  }   static showLastResult() {    console.log(Calculator.lastResult);  }} Calculator.calculate(2, 2, "+");Calculator.showLastResult();

In Ruby

RB
# Class-level state and class methods belong to the class, not the instanceclass Calculator  # @@ makes a class variable: one value shared by the class, its subclasses and every instance  @@last_result = 0   # a constant holding a table of lambdas (Ruby's anonymous functions) instead of eval  OPERATIONS = {    "+" => ->(a, b) { a + b },    "-" => ->(a, b) { a - b },    "*" => ->(a, b) { a * b },    "/" => ->(a, b) { a.to_f / b }  }   # `def self.name` defines a class method: called as Calculator.calculate, never on an instance  def self.calculate(num1, num2, operator)    @@last_result = OPERATIONS.fetch(operator).call(num1, num2)  end   def self.show_last_result    puts @@last_result  endend Calculator.calculate(2, 2, "+")Calculator.show_last_result   # 4

def self.calculate defines a method on the class object itself (classes are objects too), which is exactly what static means in JavaScript; def Calculator.calculate is the same thing in older code. @@last_result is the direct analogue of static lastResult, with one catch the docs spell out: a class variable is shared with every subclass, so a ScientificCalculator < Calculator would overwrite the same value — which is why many Rubyists keep class-level state in a plain @variable inside the self. methods instead. The a.to_f in the division lambda is there because 2 / 3 is integer division in Ruby.

Two more things you'll meet in Rails code: private above a group of methods hides them from callers outside the object, and Data.define(:name, :age) (Ruby 3.2+) writes initialize, readers, == and inspect for you when a class is mostly data.


Resources to Learn More#

  • Modules and classes — class definition, inheritance, super, visibility and mix-ins, from the official syntax reference
  • Assignment — local, instance (@), class (@@) and constant names
  • Module — attr_reader, attr_accessor, include, private
  • Data — Data.define, the modern way to declare a value object

See also