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.
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
// Creating a Classclass Dog {} // Instantiationconst Sparky = new Dog();In Ruby
# 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.newClass 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
// 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
# 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.barkWhere 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):
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
// 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
# 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) # trueIf 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
// 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
# 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 # 4def 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