Updated by: Chris Simmons, based on the 5.2 Cheatsheet by Scott Borecki.
Please reach out if you have any comments or suggestions for updates!
- This guide uses Rails version v7.1.2. Run
rails -v
to check your Rails version number. - Code snippets in this cheatsheet starting with
$
:- Run from the terminal, usually within your project directory.
- Do not include the
$
in your terminal command.
# [...]
is used within example code blocks to indicate some code may have been omitted for brevity.- Warning:
- This checklist does not necessarily rely on TDD to build up the Rails application.
- It uses some code snippets with specific practical examples, so make sure to replace them as needed for your project.
- This cheatsheet was adapted from other cheatsheets, including:
- Rails Guides v7.1.2
- Style Guides:
- Turing School:
- Getting Started
- Models and Relationships
- Updating Database Schema
- Testing
- Create Routes
- Create Controllers
- Create Views
- Rails Helpers
- From the command line, start a new rails app. For example:
$ rails new project_name -T -d='postgresql'
Command-Line Flag Description new
Tells Rails to create a new Rails application. project_name
The name of your project. -T
Tells Rails we are not going to use the default rails testing database (We will be using RSpec instead - see Step 3). -d='postgresql'
Tells Rails that we will be using a PostgreSQL database.
-
Install any gems you need by inserting the gems into the Gemfile (
project_directory/gemfile
) within thegroup :development, :test
code block. This will make the gems available in your development and test environments. For example, some handy, often-used gems:# Gemfile # [...] group :development, :test do gem 'pry' gem 'rspec-rails' gem 'capybara' gem 'launchy' gem 'simplecov' gem 'shoulda-matchers' gem 'orderly' # Some other gems end # [...]
Then run
$ bundle install
to install the newly added gems.Gem Description Docs pry A runtime developer console and IRB alternative with powerful introspection capabilities docs rspec-rails Brings RSpec testing framework to Rails docs capybara Helps you test web applications by simulating how a real user would interact with your app docs launchy Helper class for launching cross-platform applications in a fire and forget manner docs simplecov A code coverage analysis tool for Ruby docs shoulda-matchers Provides RSpec- and Minitest-compatible one-liners to test common Rails functionality that, if written by hand, would be much longer, more complex, and error-prone docs orderly Rspec matcher for asserting that this appears_before(that) in rspec request specs docs -
Run the following command in the terminal to install RSpec in your rails application (reference: rspec-rails docs):
$ rails g rspec:install
-
Add the following simplecov configuration code in the
spec/rails_helper.rb
file at the top of the file as follows (reference: simplecov docs):# spec/rails_helper.rb require "simplecov" SimpleCov.start # [...]
-
Add the following shoulda-matchers configuration code at the bottom of
spec/rails_helper.rb
(reference: shoulda-matchers docs):# spec/rails_helper.rb # [...] # Configures Shoulda-Matchers to use RSpec as the test framework and full matcher libraries for Rails Shoulda::Matchers.configure do |config| config.integrate do |with| with.test_framework :rspec with.library :rails end end
-
From the command line run
$ bundle install
. Sometimes it will be necessary to run$ bundle update
to get the latest versions of the gems.
-
To create the database in the terminal run:
$ rails db:create
- If you get a warning that database already exists, you likely need to
drop
the table thencreate
a new table. In the terminal run:$ rails db:drop $ rails db:create # Or you can chain multiple tasks in one command: $ rails db:{drop,create}
- If you get a warning that database already exists, you likely need to
-
To generate your database migrations (Rails Guides: AR Migrations) in the terminal run:
-
For "no" relationship (i.e. no foreign keys): for an example with a table called
Articles
withtitle
(withstring
datatype) andbody
(withtext
datatype) columns:# Practical Example: $ rails generate migration CreateArticles title:string body:text
-
For a "belongs to" relationship: for an example with a table called
Comments
withauthor_name
andbody
columns that "belongs to" theArticle
table via a foreign key:# Practical Example: $ rails generate migration CreateComments author_name:string body:text article:references
-
If you need to add a relationship after you migrated (i.e. add a foreign key column): for an example with a table called
Comments
that "belongs to" theArticle
table:# Practical Example: $ rails generate migration AddArticlesToComments article:references
# Practical Example: # db/migrate/[timestamp]_add_articles_to_comments.rb # [...] def change # primary table # adds foreign key to primary table add_reference :comments, :article, foreign_key: true # foreign table end
-
Check the generated migration file (within the
db/migrate/
directory) to make sure the table is being created with the specified columns and to add the timestamps stamps column at the bottom:# Practical Example: # db/migrate/[timestamp]_create_articles.rb class CreateArticles < ActiveRecord::Migration[7.0] def change create_table :articles do |t| t.string :title t.text :body t.timestamps # In Rails 7, this line gets added automatically! # This will create the created_at and updated_at timestamp columns for us. end end end
-
-
After a migration is successfully generated, run
$ rails db:migrate
in the terminal. -
Again, you can chain commands together e.g.
$ rails db:{drop,create,migrate}
. -
Note: When running migrations that add foriegn keys to existing tables with existing data, you will get a message similar to:
rails aborted!
ActiveRecord::NotNullViolation: PG::NotNullViolation: ERROR: null value in column "artist_id" of relation "songs" violates not-null constraint
This is because your existing table has data in it with null values. Rails wants the foreign keys to always be "not null" - why would a child exist that doesn't have a parent? To get around this, you should run rails db:{drop,create,migrate,seed}
to ensure the database gets cleared & then repopulated as it should be. (This is also assuming your seeds.rb
file is updated with objects that reflect that new relationship.)
Reference: Rails Guides: AR Associations
One-to-Many
- The objects on the many end (that
belong_to
the one object) should:- have a foreign key referencing the one object.
- be singular.
- For example, "an article
has_many
comments and a commentbelongs_to
an article". - The table object after
belongs_to
should be singular. - The table objects after
has_many
should be plural.
- For example, "an article
Many-to-Many
- The
joins
table should have a foreign key referencing each of the tables itbelongs_to
.
-
Create a model file (
*.rb
) inapp/models/
(e.g.app/models/comment.rb
).
For example, the many side of the relationship (belongs_to):# Practical Example: # app/models/comment.rb class Comment < ApplicationRecord belongs_to :article end
-
Create a model file (
*.rb
) inapp/models/
(e.g.app/models/article.rb
).
For example, the one side of the relationship (has_many):# Practical Example: # app/models/article.rb class Article < ActiveRecord::Base has_many :comments end
-
Create a test file (
*_spec.rb
) inspec/models/
, (e.g.spec/models/comment_spec.rb
).
For example, the many side of the relationship:# Practical Example: # spec/models/comment_spec.rb require "rails_helper" describe Comment, type: :model do describe "relationships" do it { should belong_to(:article) } end end
-
Create a test file (
*_spec.rb
) inspec/models/
, (e.g.spec/models/article_spec.rb
).
For example, for the one side of the relationship:# Practical Example: # spec/models/article_spec.rb require "rails_helper" describe Article, type: :model do describe "validations" do it { should have_many(:comments) } end end
-
When referring to a
CamelCased
table with multiple words, for exampleSongArtists
in a test or model, uselower_snake_case
(e.g.song_artists
). -
Create a model file (
*.rb
) inapp/models/
(e.g.app/models/tag.rb
), or add code if it has already been created for each side of the many-to-many relationship. Make sure to do this for each of the many-to-many models. For example the has many side of the relationship:# Practical Example: # app/models/tag.rb class Tag < ApplicationRecord has_many :taggings has_many :articles, through: :taggings end
-
Create a join model in
app/models/
, for example,app/models/tagging.rb
.
For example the join model,# Practical Example: # app/models/tagging.rb class Tagging < ApplicationRecord belongs_to :tag belongs_to :article end
-
Create a test for each has many side of the relationship, for example,
spec/models/tag_spec.rb
.
For example:# Practical Example: # spec/models/tag_spec.rb require "rails_helper" describe Tag, type: :model do describe "relationships" do it {should have_many(:tagings)} it {should have_many(:articles).through(:taggings)} end end
-
Create a test for the joins, for example,
spec/models/tagging_spec.rb
.
For example:# Practical Example: # spec/models/tagging_spec.rb require "rails_helper" describe Tagging, type: :model do describe "relationships" do it {should belong_to(:tag)} it {should belong_to(:article)} end end
Reference: Rails Guides: Changing Columns
When you need to make an edit after you have migrated, you should create a new migration.
-
Generate a new migration in the terminal.
For example, to change the datatype of thebody
column totext
in theComments
table:# Example Structure: $ rails generate migration ChangeColumnNameToBeDatatypeInTableName
# Practical Example: $ rails generate migration ChangeBodyToBeTextInComments
-
Open the migration file and put in the change. For example:
# Example Structure: # db/migrate/[timestamp]_change_column_name_to_be_datatype_in_table_name.rb class ChangeColumnNameToBeDatatypeInTableName < ActiveRecord::Migration[7.0] def change change_column :table_name, :column_name, :datatype # This is what you put within the 'change' method code block end end
# Practical Example: # db/migrate/[timestamp]_change_body_to_be_text_in_comments.rb class ChangeBodyToBeTextInComments < ActiveRecord::Migration[7.0] def change change_column :comments, :body, :text # This is what you put within the 'change' method code block end end
-
Run
$ rails db:migrate
. -
Check database schema file (
db/schema.rb
) to confirm the column data type has been updated.
Reference: Rails Guides: Add Column
-
Generate a new migration in the terminal.
For example, to add anemail
column to theComments
table:# Example Structure: $ rails generate migration AddColumnNameToTableName
# Practical Example: $ rails generate migration AddEmailToComments
-
Open the migration file and put in the change (or to confirm it was automatically generated, if the migration name follows the form
AddXXXToYYY
). For example:# Example Structure: # db/migrate/[timestamp]_add_column_name_to_table_name.rb class AddColumnNameToTableName < ActiveRecord::Migration[7.0] def change add_column :table_name, :column_name, :datatype # This will be automatically generated if migration name follows the form: "AddXXXToYYY" # Otherwise, you can manually input. end end
# Practical Example: # db/migrate/[timestamp]_add_email_to_comments.rb class AddEmailToComments < ActiveRecord::Migration[7.0] def change add_column :comments, :email, :string # This will be automatically generated if migration name follows the form: "AddXXXToYYY" # Otherwise, you can manually input. end end
-
Run
$ rails db:migrate
. -
Check database schema file (
db/schema.rb
) to confirm the new column was created.
References:Rails Guides: AR Validations, Shoulda-Matchers Docs
-
Create both a
models
andfeatures
sub-directory in thespec
folder (e.g.spec/models/
andspec/features/
).- In general,
model
tests will test the relationships, validations, and logic of the model methods. - In general,
feature
tests will test the display and functionality of the views.
- In general,
-
Framework for a
model
test:# Practical Example: # spec/models/article_spec.rb require 'rails_helper' describe Article, type: :model do describe 'validations' do it { should validate_presence_of(:title) } it { should validate_presence_of(:body) } end end
-
Framework for a
feature
test:# Practical Example: # spec/features/articles/index_spec.rb require 'rails_helper' RSpec.describe '/articles/index.html.erb', type: :feature do let!(article1) { Article.create!(title: 'Title 1', body: 'Body 1') } let!(article2) { Article.create!(title: 'Title 2', body: 'Body 2') } describe 'as a user' do describe 'when I visit the articles index' do it 'displays all the articles' do visit '/articles' expect(page).to have_content(article1.title) expect(page).to have_content(article2.title) end end end end
-
Add code into
config/routes.rb
, such as one of the following examples,# Practical Example: # config/routes.rb Rails.application.routes.draw do get '/articles/:id', to: 'articles#show' # Example of a hand-rolled route resources :articles # Example using Rails resources to generate all RESTful routes resources :articles, only: [:index, :show] # Example that only generates the specified routes resources :articles, except: [:destroy, :index] # Example that generates routes, except for the specified routes end
-
Uses
lower_snake_case
for controller names in the routes if it more than one word (e.g.monster_trucks#show
for a controller namedMonsterTrucksController
). -
Seven RESTful routes (actions):
index
,new
,create
,show
,edit
,update
, anddestroy
Route HTTP Verb URI Controller Action Description get '/articles', to: "articles#index
GET /articles index Display a list of all articles
get '/articles/new', to: "articles#new
GET /articles/new new Show form to make a new article
post '/articles', to: "articles#create"
POST /articles create Add new article
to the database, then redirectget '/articles/:id', to: "articles#show"
GET /articles/:id show Show information about a particular article
get '/articles/:id/edit', to: "articles#edit"
GET /articles/:id/edit edit Show from to edit an existing article
patch '/articles/:id', to: "articles#update"
PATCH /articles/:id update Update an existing article
, then redirectdelete '/articles/:id', to: "articles#destroy"
DELETE /articles/:id destroy Delete a particular article
, then redirect -
To display all routes in the rails application run:
$ rails routes
-
To display all routes for a specific controller in the rails application, run:
# Example Structure: $ rails routes -c resource_name
# Practical Example: $ rails routes -c articles # Alternate Example: $ rails routes -c Article
-
Create a controller file (
*_controller.rb
) inapp/controllers/
For example, to create a controller calledArticlesController
, this should look likeapp/controllers/articles_controller.rb
. -
Add in the framework for a controller, for example
# Practical Example: # app/controllers/articles_controller.rb class ArticlesController < ApplicationController end
- Create directory under
app/views
named after the controller (e.g.app/views/articles
) and with a file for the view, (e.g.index.html.erb
). This should look likeapp/views/articles/index.html.erb
.
Reference: Rails Guides: Form Helpers
-
For a search:
<!-- Practical Example: --> <%= form_with url: '/search', method: 'get', local: true do |form| %> <%= form.label :q %> <%= form.text_field :q %> <%= form.submit "Search" %> <% end %>
-
For a create, using a model url:
<!-- This example needs updating for the first line --> <!-- Consider using the form with model helper --> <!-- Practical Example: --> <%= form_with url: path, local: true do |f| %> <%= f.label :name %> <%= f.text_field :name %> <%= f.label :breed %> <%= f.text_field :breed %> <%= f.label :age %> <%= f.number_field :age %> <%= f.submit %> <% end %>
References: Rails Guides: Validations, Shoulda-Matchers Docs
-
Use
validates
in the model.
For example:# Practical Example: # app/models/article.rb class Article < ApplicationRecord validates :title, presence: true validates :body, presence: true # [...] end
-
Use
validates_presence_of
in model tests.
For example:# Practical Example: # spec/models/article_spec.rb require 'rails_helper' describe Article, type: :model do # [...] describe 'validations' do it { should validate_presence_of(:title) } it { should validate_presence_of(:body) } end # [...] end
Reference: Rails Guides: Path Helpers
- Use path helper with verb (e.g.
method: delete
) if the path helper is ambiguous.
For example:<!-- Practical Example: --> <%= link_to "Delete", article_path(@article), method: :delete %>
Credit given to Scott Borecki for original 5.2 version: [GitHub]: https://github.com/scott-borecki [gmail]: mailto:[email protected] [LinkedIn]: https://www.linkedin.com/in/scott-borecki/