Ruby, ORM, & ActiveRecord — Beginners Guide

Hannah McCullough
4 min readJan 18, 2022

When I was first introduced to Ruby a week and a half ago, I struggled with understanding the concepts of ORMs, specifically speaking, ActiveRecord, and how it relates to the OO-language and databases. As a result, I did a lot of research and would like to share, in Layman’s terms, my understanding. Enough introduction; you get the point already, so let’s get down to basics.

What is ORM?

ORM is an acronym for “object relational mapping” and essentially, it is an organizational design pattern that allows us to map objects within our program to relational tables in a database.

In other words, ORM is a framework that acts as the middleman between your OO-language objects (in my case, Ruby) and the database by “mapping” the classes to database tables, attributes to columns, and instances of classes to rows in those tables — see below.

Why ORM?

Why have a middleman? There are a handful of reasons to use an ORM, but we’ll discuss the two main reasons:

  1. It minimizes repetitive code
  2. Organization that makes sense

Minimizes Repetitive Code

For example, each time you need to insert a new dog into our example table above, we would have to type out the following:

A command to create a connection to our database:

database_connection = SQLite3::Database.new('db/dogs.db')

Create an owners table and a dogs table:

database_connection.execute("CREATE TABLE IF NOT EXISTS dogs(id INTEGER PRIMARY KEY, breed TEXT, name TEXT, age INTEGER, favorite_thing TEXT, favorite_treat TEXT, owner_id INTEGER)")

database_connection.execute("CREATE TABLE IF NOT EXISTS owners(id INTEGER PRIMARY KEY, name TEXT)")

Then we would have to repeatedly insert new dogs and owners into said tables:

database_connection.execute("INSERT INTO dogs (breed, name, age, favorite_thing, favorite_treat, owner_id) VALUES ('miniature schnauzer' 'Phoenix, 3, 'fetch', 'turkey', 3)")

That’s a lot to type over and over again! Instead, we can create an ORM and write a #save method on our Dog class that handles the common action of INTERTing data into the database, like so:

class Dog

@@all = []

attr_reader :breed, :name, :age, :favorite_thing, :favorite_treat, :owner_id

def initialize(breed, name, age, favorite_thing, favorite_treat, owner_id)
@breed = breed
@name = name
@age = age
@favorite_thing = favorite_thing
@favorite_treat = favorite_treat
@owner_id = owner_id
@@@all << self
end

def self.all
@@all
end

def save(database_connection)
database_connection.execute("INSERT INTO dogs(breed, name, age, favorite_thing, favorite_treat, owner_id) VALUES (?, ?, ?, ?, ?, ?)", self.breed, self.name, self.age, self.favorite_thing, self.favorite_treat, self.owner_id)
end
end

Then, to insert new dogs and save them to the database table, all we have to type is:

database_connection = SQLite3::Database.new('db/dogs.db')

Dog.new("miniature schnauzer", "Phoenix", 3, "fetch", "turkey", 3)
Dog.new("frenchie", "Phoebie", 4, "snorting", "everything", 5)

Dog.all.each do |dog|
dog.save(database_connection)
end

Now we have reusable code that allows us to type less! The above #save method is just one of many examples of how an ORM can make a developer’s life easier.

Organization that makes sense

As we already mentioned, an ORM relays our OO-language objects to a relational database table — this is the sensical organization that comes with an ORM. Instead of developer’s communicating with the database directly, the ORM is doing the bang-my-head-against-the-wall work for us. The ORM creates the organization of relating classes to tables, attributes to table columns, and instances of the class to the table rows — it just makes sense.

Bird’s-eye View of ActiveRecord

Last, but absolutely not least, we have ActiveRecord. ActiveRecord is an ORM library (Ruby gem) that allows you to use its simplified commands to call SQL functions with ease and headache-free, once you get the hang of it, that is. In essence, ActiveRecord saves us from having out write out our own custom ORMs and it does the hard work under the hood for us. This is accomplished by inheriting pre-defined functionality and features from ActiveRecord. Let’s take a look at how we can accomplish this.

First, we need to tell ActiveRecord where our desired database is by establishing a connection:

ActiveRecord::Base.establish_connection(
adapter: "sqlite3",
database: "db/dogs.sqlite"
)

If our dogs table doesn’t exist, create a table:

sql = <<-SQL
CREATE TABLE IF NOT EXISTS dogs (
id INTEGER PRIMARY KEY,
breed TEXT,
name TEXT,
age INTEGER,
favorite_thing TEXT,
favorite_treat TEXT,
owner_id FOREIGN KEY
)
SQL

ActiveRecord::Base.connection.execute(sql)

Then, we need to link a Dog “model” to the dogs database table:

class Dog < ActiveRecord::Base
end

Now we can easily communicate with our dogs database table using ActiveRecord methods such as, .all, .find, .create, column_names, and more!

Dog.create("frenchie", "Phoebie", 4, "snorting", "everything", 5)
# INSERT INTO dogs (breed, name, age, favorite_thing, favorite_treat, owner_id)
# => #<Dog:0x00007f985d0638b0 id: 1, breed: "frenchie", name: "Phoebie", age: 4, favorite_thing: "snorting", favorite_treat: "everything", owner_id: 5>

This article only touches the tip of the iceberg on the connections between Ruby, ORMs, and ActiveRecord. To further explore these topics and their concepts, check out the resources below!

Resources:
Flatiron School’s Phase 3 Material

Active Record Basics

Ruby Programming Language

--

--