Introduction to ActiveRecord
Reading
Pre-work
To get the most of out using Ruby for databases, we should do some configuration
of our irb
sessions. This will make printing of database objects more
awesome
To achieve this we will install a gem
and configure our ~/.irbrc
file to
configure our console with some nice features.
- Step 1: Install
awesome_print
gem install awesome_print
- Step 2: Edit a file
~/.irbrc
and update it's content
If not code
, use your favorite editor
code ~/.irbrc
Add this content. Don't worry too much about what it does, or how it works.
#!/usr/bin/rubyrequire 'irb/completion'require 'irb/ext/save-history'# Require awesome_print in our environmentbeginrequire 'awesome_print'rescue LoadErrorend# Keep an IRB historyIRB.conf[:SAVE_HISTORY] = 1000IRB.conf[:HISTORY_FILE] = "#{ENV['HOME']}/.irb_history"# Simple prompt modeIRB.conf[:PROMPT_MODE] = :SIMPLE# Configure awesome print if availableif defined?(AwesomePrint)require 'awesome_print/ext/active_support'require 'awesome_print/ext/active_record'AwesomePrint.irb!AwesomePrint.defaults = {plain: false,index: false,ruby19_syntax: true,color: {hash: :pale,class: :white,bigdecimal: :yellow,integer: :yellow,fixnum: :yellow,symbol: :yellow,},indent: -2}end# If we are in Railsif ENV['RAILS_ENV'] || defined?(Rails)# Customize the IRB promptapp_name = Rails.application.class.parent_name.downcaseapp_env = Rails.env[0...3]IRB.conf[:PROMPT] ||= {}IRB.conf[:PROMPT][:RAILS] = {PROMPT_I: "#{app_name}> ",PROMPT_S: "#{app_name}* ",PROMPT_C: "#{app_name}? ",RETURN: "=> %s\n"}IRB.conf[:PROMPT_MODE] = :RAILSend
Database setup
- Step 1: Make sure you have these tables created. You likely have these created if you have previously followed this lesson
CREATE TABLE movies (id SERIAL PRIMARY KEY,title TEXT NOT NULL,primary_director TEXT,year_released INT,genre TEXT);CREATE TABLE ratings (id SERIAL PRIMARY KEY,rating TEXT);ALTER TABLE movies ADD COLUMN rating_id INTEGER NULL REFERENCES ratings (id);CREATE TABLE actors (id SERIAL PRIMARY KEY,full_name TEXT NOT NULL,birthday DATE);CREATE TABLE roles (id SERIAL PRIMARY KEY,movie_id INTEGER REFERENCES movies (id),actor_id INTEGER REFERENCES actors (id));
- Step 2: See this lesson for
INSERT INTO
statements to populate the database
Using bundler
Bundler is the main package manager for Ruby. It is like npm.
In most Ruby applications we will be using Bundler for managing the dependencies of our code. Bundler is a very powerful tool that can add libraries to your code from the main repository at rubygems or from Github. Bundler also allows you to require some libraries in development mode, some in test, and others in production. It also has a very powerful and flexible version requirement langauge.
For now we are going to use Bundler in it's
inline
mode since we'll only be dealing with a single Ruby file. Soon we will use
Bundler in it's typical mode by using a
Gemfile
Use bundler to require the Postgres and ActiveRecord libraries
Create a ruby script (movies.rb
) to contain our Ruby code
require 'bundler/inline'gemfile dosource 'https://rubygems.org'gem 'pg'gem 'activerecord'endrequire 'active_record'
Basic Active Record Setup
- NOTE: If you did not use the name
suncoast_movies
, replace"suncoast_movies"
with the name of your database where themovies
,actors
, androles
tables were created.
# Tell Ruby we are going to use Bundlerrequire 'bundler/inline'# Define which libraries to usegemfile do# Use the official source (rubygems)source 'https://rubygems.org'# Use postgres librarygem 'pg'# Use ActiveRecord ORMgem 'activerecord'end# Load up the Active Record coderequire 'active_record'# When ActiveRecord needs to log something, like what SQL# it is generating, send it to "STANDARD OUTput" (our terminal)ActiveRecord::Base.logger = Logger.new(STDOUT)# Hey ActiveRecord, establish a connection to our databaseActiveRecord::Base.establish_connection(adapter: "postgresql",database: "suncoast_movies")
Setup models
class Movie < ActiveRecord::Baseendclass Rating < ActiveRecord::Baseend
Query
- Get all the movies
Movie.all
- Get the first movie
Movie.first
- Get the last movie
Movie.last
- Get a specific movie by id
Movie.find(42)
- Get a specific movie by title
Movie.find_by(title: "Star Wars")
- Get all movies released in 1984
Movie.where(year_released: 1984)
- Get all movies released after 1984
Movie.where("year_released > ?", 1984)
- Get all movies where the name contains
aliens
Movie.where("title like ?", "%aliens%")
Insert
Movie.create(title: "SDG: The Adventure", primary_director: "Suncoast", year_released: 2018, genre: "code")
Delete
Movie.where("year_released > ?", 1984).delete_all
movie = Movie.find(42)
thenmovie.delete
Update
movie = Movie.find(4)
movie.title = "New Title"
movie.save
movie.update_attributes(title: "Newer Title", year_released: 2007)
Join
- Update the
Movie
model
class Movie < ActiveRecord::Basebelongs_to :ratingend
Now we can write statements like these:
movie = Movie.firstp movie.rating
- If we now extend the
Rating
model like this:
class Rating < ActiveRecord::Basehas_many :moviesend
we can write queries such as:
rating = Rating.firstp rating.movies
Many to Many Join
- We must define a model for the
Role
model that joins theMovie
model and theActor
model
class Actor < ActiveRecord::Baseendclass Role < ActiveRecord::Basebelongs_to :moviebelongs_to :actorend
- Now we can revise
Movie
andActor
to use theRole
model as our join model, we call thishas_many :through
class Actor < ActiveRecord::Basehas_many :roleshas_many :movies, through: :rolesendclass Movie < ActiveRecord::Basehas_many :roleshas_many :actors, through: :rolesend
- And we can write code such as:
movie = Movie.find_by(title: "The Lord of the Rings: The Two Towers")p movie.actorsactor = Actor.find_by(full_name: "Martin Freeman")p actor.movies
Sneak peek at using this for APIs
Let's get all the Movie
objects and print the resulting array of these movies
as JSON.
movies = Movie.allmovies_as_a_hash = movies.as_jsonputs JSON.pretty_generate(movies_as_a_hash)
The movies.as_json
turns the collection of ActiveRecord objects into a Ruby
hash
and then JSON.pretty_generate
formats a nice, human readable, JSON
string. Finally puts
prints the results.
If we were doing this all at once this could look like:
puts JSON.pretty_generate(Movie.all.as_json)
Additional ActiveRecord
resources
- Active Record Basics
- Sections 1, 2, 3, 5
- Active Record Querying
- Sections 1, 2, 3, 4, 5
- Active Record Associations
- Sections 1, 2