Ruby
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Methods and Blocks
-
Section 4: Object-Oriented Ruby
-
Section 5: Metaprogramming
-
Section 6: Working with Data and Files
-
Section 7: Ruby Frameworks Overview
-
Section 8: Ecosystem and Testing
-
Section 9: Practical Projects
-
Section 10: Interview Practice
-
Section 11: Data Structures and Algorithms in Ruby
-
Section 12: More Practice Exercises
-
Section 13: Enumerable and Functional Style
-
Section 14: More OOP Practice
-
Section 15: More Testing
-
Section 16: Enumerable and Comparable Modules In Depth
-
Section 17: Ruby Standard Library: Core Utilities
-
Section 18: Ruby Standard Library: Data and Security
-
Section 19: Ruby Standard Library: CLI and Text
-
Section 20: Ruby Networking
-
Section 21: Ruby on Rails Deep Dive
-
Section 22: Ruby Metaprogramming Deep Dive
-
Section 23: Ruby Design Patterns
-
Section 24: Ruby Concurrency
-
Section 25: Ruby Testing Deep Dive
-
Section 26: Ruby Gems and Packaging
-
Section 27: Ruby Performance
-
Section 28: More Practice Exercises
-
Section 29: More Interview Practice
-
Section 30: Rails API Development
-
Section 31: Rails Authentication and Authorization
-
Section 32: Rails Testing Deep Dive
-
Section 33: Rails Performance
-
Section 34: Rails Deployment
-
Section 35: Sinatra and Lightweight Ruby Web Apps
-
Section 36: More Ruby Language Deep Dive
-
Section 37: Ruby 3.x Modern Features
-
Section 38: More Data Structures in Ruby
-
Section 39: More Practical Projects
-
Section 40: Ruby Ecosystem Tools
-
Section 41: More Practice and Review
-
Section 42: Final Practice and Mastery
-
Section 43: Ruby Interview Deep Dive
-
Section 44: Ruby Background Processing Deep Dive
-
Section 45: Ruby GraphQL
-
Section 46: Ruby Object Model Deep Dive
-
Section 47: Ruby Hanami Framework Overview
-
Section 48: Ruby gRPC and Protocol Buffers
-
Section 49: Ruby Data Processing
-
Section 50: Ruby Search Integration
-
Section 51: Ruby File Upload and Media
-
Section 52: Ruby Email and Notifications
-
Section 53: Ruby Admin Panels
-
Section 54: Ruby Feature Flags and Experimentation
-
Section 55: Ruby Monitoring and Observability
-
Section 56: Ruby Docker and Deployment Deep Dive
-
Section 57: Ruby Security Deep Dive
-
Section 58: More Advanced Metaprogramming
-
Section 59: More Final Projects
27: ActiveRecord Basics
Up until now, we've been handling data in memory. That's fine for a script that runs for ten seconds, but it's useless for a real application because everything vanishes the moment the process exits. This is where ActiveRecord comes in. It's an ORM—Object-Relational Mapper—which is a fancy way of saying it lets us treat database rows as if they were regular Ruby objects.
I've always found ActiveRecord to be one of the most "magical" parts of the Ruby ecosystem. It does a lot behind the scenes, which is great until it isn't. To get a handle on it, let's build a simple reading list tracker. We want to keep track of books, their authors, and whether we've actually finished reading them.
Setting up the Book model
First, we need a class that inherits from ActiveRecord::Base. This inheritance is what gives our class all the power to talk to the database without us writing a single line of SQL.
require 'active_record'
# For this example, we'll use an in-memory SQLite database so you don't
# have to set up a database server on your machine.
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
class Book < ActiveRecord::Base
end
Now, the model is just a Ruby class, but it doesn't know what the database table looks like yet. In a full Rails app, we'd use migration files. Since we're keeping this lean, I'll define the schema directly in the script.
ActiveRecord::Schema.define do
create_table :books do |t|
t.string :title
t.string :author
t.boolean :read, default: false
end
end
Adding and finding books
Now that the table exists, we can start playing with data. The .create method is the quickest way to instantiate an object and save it to the database in one go.
Book.create(title: "The Hobbit", author: "J.R.R. Tolkien")
Book.create(title: "Foundation", author: "Isaac Asimov")
Book.create(title: "Neuromancer", author: "William Gibson")
# Let's grab a specific book by its ID
book = Book.find(1)
puts "Found: #{book.title}" # Output: Found: The Hobbit
I personally prefer find_by over find when I don't have the ID handy. find raises an exception if the record isn't found, which can crash your app if you aren't careful. find_by just returns nil.
The "Collection vs. Object" trip-up
Here is where I almost tripped up when I first started with ActiveRecord. I wanted to mark "Foundation" as read. I tried this:
# This is my mistake:
Book.where(title: "Foundation").update(read: true)
# NoMethodError: undefined method `update' for #<ActiveRecord::Relation...>
I forgot a fundamental rule: .where always returns an ActiveRecord::Relation—which is essentially a collection (like an array)—even if only one record matches. You can't call update on a collection; you have to call it on a single instance of a model.
To fix this, I need to tell ActiveRecord I want the first matching record from that collection:
# The correct way:
Book.where(title: "Foundation").first.update(read: true)
Filtering and querying
The real power of ActiveRecord is how it handles queries. Instead of writing SELECT * FROM books WHERE read = false, we can use Ruby methods. I find that chaining these methods makes the code read almost like a sentence.
# Get all books we haven't read yet
unread_books = Book.where(read: false)
unread_books.each do |book|
puts "#{book.title} is still waiting to be read!"
end
If you ever wonder what ActiveRecord is actually doing under the hood, you can look at your logs or use .to_sql on a query. It's a great way to make sure you aren't accidentally writing a query that will destroy your database performance once you have a million rows.
📋 Practical Task
Building a Book Archive Manager
Your task is to extend the reading list example into a small archive manager. Create a script that does the following:
- Sets up an in-memory SQLite database and a
Bookmodel withtitle,author, andread(boolean) columns. - Seeds the database with at least four different books.
- Implements a "Mark as Read" feature: Use
find_byto locate a specific book by its title and update itsreadstatus totrue. - Implements a "Cleanup" feature: Use
destroy_allto remove all books from the database that have already been read. - Finally, print the titles of the remaining books in the database to verify the cleanup worked.
There are no comments for now.