The Ultimate Ruby on Rails Cheatsheet

Digestible Ruby on Rails information to help you with development.

The Ultimate Ruby on Rails

Cheat Sheet & Code Snippets


Create a Rails Application

rails new my-app

See the Options Available for Creating a Rails Application

rails new --help

Start Rails Server

rails s

Start Rails Console

rails c

Start Rails Console without Database Saving

This stops changes from saving while in the rails console

rails c --sandbox

View and Search Rails Routes in the Browser


View Application Information in the Browser



Install Ruby Gems in Gemfile

bundle install

Install Ruby Gem

bundle add devise

Update ALL Ruby Gems

bundle update

Update Ruby Gem

bundle update devise

Remove Ruby Gem

bundle remove devise

Execute Bundle Command in Context of Application Gemfile

bundle exec ...


Create Controller

Creates a controllers/pages_controller.rb with the home and about action with view files

rails g controller pages home about

Create Migration

Creates the migration to add the column publish_date (date) to the projects table

rails g migration add_publish_date_to_projects publish_date:date

Create Model

Creates a models/project.rb file and a migration to create the projects table with the columns title (string) and body (text)

rails g model project title body:text

Create Scaffold

Does everything that rails g controller and rails g model would do

rails g scaffold projects title body:text

Create Rake Task

Creates a lib/rake/projects.rake file that includes the trim_title method, you can call this method with rake projects:trim_title

rails g task projects trim_title

Create Mailer

Creates a mailers/user_mailer.rb file with a thanks_for_joining action with corresponding view files; views/user_mailer/thanks_for_joining.txt.erb and views/user_mailer/thanks_for_joining.html.erb

rails g mailer user thanks_for_joining


Think of destroy as the opposite of generate. It'll figure out what generate did, and undo it

Destroy Controller

rails d controller Pages

Delete Model

rails d model Article

Delete Scaffold

rails d scaffold Projects

Delete Mailer

rails d mailer User


Create the Database

rails db:create

Drop the Database

rails db:drop

Migrate the Database

rails db:migrate

Get Statuses of All Database Migrations

rails db:migrate:status

Rollback Last Database Migration

rails db:rollback

Rollback Multiple Database Migrations

rails db:rollback STEP=5

Redo Database Migration

This does the following; rollback and migration the database

rails db:migrate:redo

Runs db:rollback and db:migrate

Seed the Database

Runs the db/seeds.rb file

rails db:seed

Reset the Database

This does the following; drops, create, migrates and seeds the database

rails db:reset

Change Database to PostgresQL

rails db:system:change --to=postgresql

Rest Database Table ID Count


Reset ALL Database Tables ID Count

ActiveRecord::Base.connection.tables.each do |t|

Populate a New Table in a Database Migration

class CreatePostCategories < ActiveRecord::Migration[5.0]
	def up
		t.integer :id
        t.string :name



	%w[news blog insight guide].each do |category|
        PostCategory.create(name: category)

	def down
        drop_table :post_categories

Reset Cached Information in Table Columns



Set Root

root to: 'pages#home'

Create a Route

Creates an /about path that maps to the controller/pages_controller.rb about action

get 'about', to: 'pages#about'

Map the /about route to the PagesController about action (creates a about_path helper)

Create a Route with a Named Route Helper

Reference the /about path with about_us_path

get 'about', to: 'pages#about', as: 'about_us'

Same as the above but this time the helper is about_us_path

CRUD Routes

This creates the full CRUD routes; index, show, new, create, edit, update and destroy

resources :projects

CRUD Routes (Only)

This only creates the index and show action routes

resources :projects, only: %i[index show]

CRUD Routes (Except)

This creates all except the index and show action routes

resources :projects, except: %i[index show]

Non ID Resource

Use this when the resource doesn't need an ID lookup, in this example the route /profile would be the show action route

This creates all the CRUD routes, minus the index action route

resource :profile

Prefix URL and Controller

This will create routes like admin/projects which map to the controller controller/admin/projects_controller.rb and the views would be located at views/admin/projects

namespace :admin do
	resources :projects

Prefix URL without Changing Controller

This will create routes like admin/projects but the controller would be still be controller/projects_controller.rb and the views will be located at views/projects

scope :admin do
	resources :projects

Create Route for Object

This will create the route projects/search

resources :projects do
	collection do
		get "search"

Create Route for Individual Records

This will create the route projects/:id/complete

resources :projects do
	member do
		put "complete"

Add 301 Redirect

get 'about', to: redirect('about-us')

301 Moved Permanently - HTTP | MDN

Add 308 Redirect

get 'about', to: redirect('about-us')

308 Permanent Redirect - HTTP | MDN


Run Code Before Action

before_action :authenticate_user

Run Code Before Action on Specific Actions

before_action :authenticate_user, only: %i[create update destroy]

Redirect Back with Fallback Location

redirect_back(fallback_location: root_path)


Validate Presence of Attribute

validates :title, **presence**: true

Validate Formatting of Email

validates :email, **format**: URI::MailTo::EMAIL_REGEXP

Validate Attribute Value is Unique

validates :title, **uniqueness**: true

Validate Attribute Length is Within a Range

validates :title, length: { minimum: 60, maximum: 120 }

Set a CONST Variable

This can then be referenced as Model::COUNTRIES where Model is the class name

COUNTRIES = %w[England France Germany

Create a Scope

This allows you to call

You can also use methods, but scopes are recommend when they can be used

scope :live, -> { where(draft: false) }

Set Default Scope (NOT Recommended)

default_scope { order(created_at :desc) }

Callback Method on Action

This runs the set_publish_date method before the record is created

before_create :set_publish_date


def set_publish_date
	self.publish_date =

There are a lot of different call back methods, you can see the full list here:

Active Record Callbacks - Ruby on Rails Guides

Be careful when when using update in a callback as it can cause an infinite loop, to avoid this use update_columns

Query TRUE/FALSE on Boolean Column

Attributes that are stored as a boolean can be queried if TRUE or FALSE by appending "?"



Setup Nested Attributes

has_many :tags

accepts_nested_attributes_for :tags

Prevent Record Creation if ALL Nested Attributes are Blank

has_many :tags

accepts_nested_attributes_for :tags, reject_if: :all_blank

Allow Records from Nested Attributes to be Destroy

has_many :tags

accepts_nested_attributes_for :tags, allow_destroy: true

Delegate Attributes to Model in a Relationship (Law of Demeter)

The "Law of Demeter" is the process of chaining method calls across objects

The Problem

Fix #1 - Model Methods

This works, but you could cause the model from growing out of control

# models/post.rb

def user_name

def user_email

Fix #2 - Delegate

It's a nice idea to pass allow_nil: true so nil is returned if the data is not available

# models/user.rb

delegate :name, :email, to: :post, allow_nil: true

You can then do:


Deleting Records without Callbacks (Fast but Risky)

This is the fastest way to delete records, but it skips callbacks


Deleting Records with Callbacks (Slow but Safe)

This is the slower way to delete records, but it doesn't skip callbacks and isn't that slow



Return All Records of Object


Find Record by ID


Find Record by ID (Won't Crash if NIL)

Project.find_by(id: 10)

Find Record by ID (ID from Params)


Find Records by Attribute Value

Project.find_by(title: 'Hello')

Return Records Matching the Query

This accepts multiple column/value combinations

Project.where(draft: false)

Return Records NOT Matching the Query

This accepts multiple column/value combinations

Project.where.not(draft: false)

Return Number of Records in Database Table


Find the First, Second, Third, Fourth, Fifth, Forty Second and Last Record


"Accessing the Reddit" with Ruby on Rails

Exclude Current Record from Query

Project.where(live: true).without(self)


Add File Attachment to Model

has_one_attached :image

Check if File Attachment is Present


Delete a File Attachment from Active Storage



Add ActionText to Model

has_rich_text :body

Convert ActionText Content to Plain Text

This is useful for truncating to create an excerpt


Expose ActionText Content on Model

This will allow you to query the content in SQL, useful for searching

has_one :action_text_rich_text, class_name: 'ActionText::RichText', as: :record

		.where('action_text_rich_texts.body LIKE :query', query: "%#{query}%")

This will search the database for records where body is similar to the value of query


Render Data

<%= @project.title %>

Render HTML Data

<%== @project.title %>

Render Data

<%= @project.title %>

Set Variable/Prop

<% background_color = "#000" %>

Assign Variable/Prop

<% bg_color = local_assigns.fetch(:background_color)

Assign Variable/Prop with Default Value

<% bg_color = local_assigns.fetch(:background_color, "#000")

Render Page Content

<%= yield %>

Render Content from Views into Layout

Add a <%= yield(...) %> helper layout file: views/layouts/application.html.erb

<%= yield(:head) %>

Pass content into a <%= content_for(...) %> with matching name to the helper yield

<% content_for(:head) do %>
    <meta name="turbolinks-visit-control" content="reload" />
<% end %>

Link to Page

<a href="/about">About</a>

<%= link_to("About", about_path) %>

Link to Page with HTML Attributes

<a href="/about" class="button">About</a>

<%= link_to("About", about_path, class: "button") %>

Same as the above but with HTML attributes included

Link to Record

<a href="/projects/10">Project title</a>

<%= link_to(@project.title, project_path(@project)) %>

Link to Record (Short)

<a href="/projects/10">Project title</a>

<%= link_to(@project.title, @project) %>

Delete Record (Link)

<%= link_to("Delete", @project, method: :delete) %>

Delete Record (Form - Recommended)

<%= button_to("Delete", @project, method: :delete) %>

Back Link

<%= link_to("Back", :back) %>

Create Object Link with Array (Admin)

<a href="admin/projects/">Projects</a>

<%= link_to("Projects", [:admin, :projects]) %>

This is the equivalent of:

<%= link_to("Project", admin_projects_path) %>

Create Record Show Link with Array (Admin)

<a href="admin/projects/10">Project</a>

<%= link_to("Project", [:admin, @project]) %>

This is the equivalent of:

<%= link_to("Project", admin_project_path(project)) %>

Create Record Edit Link with Array (Edit/Admin)

<a href="admin/projects/10/edit">Edit</a>

<%= link_to("Edit", [:edit, :admin, @project]) %>

This is the equivalent of:

<%= link_to("Edit", edit_admin_project_path(project)) %>

Create Email Link

<a href=""></a>

<%= mail_to("") %>

Create Email Link with Link Text

<a href="">Email me</a>

<%= mail_to("", "Email me") %>

Render Partial

<%= render "shared/header" %>

Render Partial with Variables/Props

<%= render("shared/header, title: "Hello World!") %>

Loop Through Object

<% @projects.each do |project| %>
    <%= project.title %>
<% end %>

Render Collection

<%= render(@projects) %>

This is the equivalent of:

<% @projects.each do |project| %>
    <%= render("project", project: project)
<% end %>

Conditional Render (IF)

<% if @project.draft? %>
<% end %>

Conditional Render (Inline IF)

<%= render("download") if %>

Conditional Render (IF/ELSE)

<% if @project.draft? %>
<% else %>
<% end %>

Conditional Render (IF/ELSIF/ELSE)

<% if @project.draft? %>
<% elsif @project.scheduled? %>
<% else %>
<% end %>

Conditional rendering based on the value of @project.draft? and @project.scheduled?

Alternate CSS Classes in Loop

<% @projects.each do |project| %>
	<div class="<%= cycle("odd-class", "even-class") -%>">
		<%= project.title %>
<% end %>

Pluralize Word

This will render "1 project" or "2 projects"

<%= pluralize(@projects.count, 'project') %>

Change "project" to "projects" if @projects.count > 1

Pluralize Word with Specific Plural Word

This will render "1 person" or "2 users"

<%= pluralize(@users.count, 'person', plural: 'users') %>

Truncate Text

<%= truncate("...")

Truncate Text at Specific Length

<%= truncate("...", length: 50)

Truncate HTML

This will render the HTML tags as a string

<%= truncate("<div>...</div>", escape: false)

Created HTML ID from Record

This will create project_10


Created HTML ID from Record with Prefix

This will create edit_project_10

dom_id(@project, :edit)

Flash Messages

Add More Flash Types


add_flash_types :error, :success

Render Flash Message

<div class="alert alert-notice" role="alert">
    <%= flash[:notice] %>

<div class="alert alert-alert" role="alert">
    <%= flash[:alert] %>

Render Flash Messages with Dynamic Class

<% flash.each do |type, message| %>
    <div class="alert alert-<%= type %>" role="alert"> <%= message %> </div>
<% end %>


Fragment Caching

<%= @projects.each do |project| %>
    <% cache(project) %>
    <% end %>
<% end %>

Collection Caching

<%= render(partial: "project", collection: @projects, cached: true) %>

Fragment and Collection Caching in Ruby on Rails


Render Form Partial


You should use this on the new and edit views to keep them DRY

<%= render("form", project: @project) %>

Form with Model

<%= form_with(model: project) do |form| %>
    <%= form.label(:title) %>
    <%= form.text_field(:title) %>
    <%= form.submit %>
<% end %>

Non-Remote Form

This will stop the form submitting with JavaScript

<%= form_with(..., local: true) do |form| %>
<% end %>

Working with JavaScript in Rails - Ruby on Rails Guides

Search Form

<%= form_with(url: search_path, method: :get, local: true) do |form| %>
    <%= form.label(:q, "Search") %>
    <%= form.search_field(:q, placeholder: "Search...") %>
    <%= form.submit("Search") %>
<% end %>

Namespace Form URL

This will create the URL admin/projects

<%= form_with(model: [:admin, project]) do |form| %>
<% end %>

Form Select

Data supplied from a CONST variable on the User model

<%=, User::COUNTRIES) %>

Form Select with Prompt Option

<%=, User::COUNTRIES, include_blank: "Select country") %>

Form Select with HTML Attributes

<%=, User::COUNTRIES, {}, { class: "form-control" }) %>

Created a Nested Form

<%= form.fields_for(:projects) do |project_fields| %>
    <%= render "project/form", form: project_fields %>
<% end %>

Remove Generated ID from Nested Form

<%= form.fields_for(:projects, include_id: false) do |project_fields| %>
    <%= render "project/form", form: project_fields %>
<% end %>

Showing Form Errors in the View

Base HTML to use for form errors:

<div role="alert">
  <h2> There's <%= pluralize(errors.count, 'error') %> to fix </h2>

    <% errors.full_messages.each do |error| %>
        <li><%= error %></li>
    <% end %>

The error message here will render like:

Name can't be blank
Email is invalid

Render this with:

<%= render("components/form/errors", errors: MODEL.errors) %>


Email Address with Name

This will create the following string John Doe <>

If now name is passed it will return the email address only


Cookies & Sessions

Create Session Cookie

Session cookies will be removed once the session is over (page closes)

session[:user_id] =

Delete Session Cookie


Create Cookie

cookies[:user_id] =

Delete Cookie


Signed Cookie

cookies.signed[:user_id] =

Encrypted Cookie

cookies.encrypted[:user_id] =

Set Cookie Expiration Date

cookies[:seen_newsletter_popup] = {
    value: "true",
    expires: 10.days

Permanent Cookie

This cookie will last 20 years

cookies.permanent[:seen_newsletter_popup] = "true"

Demystifying Cookie Security in Ruby on Rails 6


Return Length of String

"Hello World".size # 11

"Hello World".length # 11

Check if String Includes Text

"Hello World".includes?("Hello") # true

"Hello World".includes?("Goodbye") # true

Replace Part of a String

"Hello World".gsub("Hello", "Goodbye") # Goodbye World

Split String into Array

"Hello World".split # ["Hello", "World"]

"Simon, Jay, William".split(",") # ["Simon", " Jay", " William"]

String Interpolation

name = "Simon"

"Hello #{name}"


Return Length of Array

["Hello", "World"].size # 2

["Hello", "World"].length # 2

["Hello", "World"].count # 2

Push to Array

["Hello", "World"] << "Goodbye" # ["Hello", "World", "Goodbye"]

["Hello", "World"].push("Goodbye") # ["Hello", "World", "Goodbye"]

Combine Arrays

["Hi", "Hello"].concat(["Bye", "See Ya"]) # ["Hi", "Hello", "Bye", "See Ya"]

["Hi", "Hello"] + ["Bye", "See Ya"] # ["Hi", "Hello", "Bye", "See Ya"]

Remove from Array

["Hello", "World"].delete("Hello") # World

["Hello", "World"].delete_at(1) # World

Remove All from Array

["Hello", "World"].clear # []

Check if Array Includes Text

["Hello", "World"].includes?("Hello") # true

["Hello", "World"].includes?("Goodbye") # true

Reverse Array

["Hello", "World"].reverse # ["World", "Hello"]

Shuffle the Array

Return a random order of the array

["Simon", "Jay", "William"].shuffle

Sample the Array

Return a random item from the array

["Simon", "Jay", "William"].sample

Remove Duplicate Items from Array

["A", "A", "B", "C", "C"].uniq # ["A", "B", "C"]

Flatten Array

[["Hi", "Hello"], ["Bye", "See Ya"]].flatten # ["Hi", "Hello", "Bye", "See Ya"]

Join Array Items (String)

["Simon", "Jay", "William"].join(", ") # Simon, Jay, William

Array Items to Sentence (String)

["Simon", "Jay", "William"].to_sentence # Simon, Jay, and William


Symbol to Proc

Capitalize Each Item in Array

["hello", "world"].map(&:capitalize) # ["Hello", "World"]

Find Current Controller Name


Find Current Controller Action Name


Remove Empty Values from Array

Returns a new array with empty values removed

["Hello", "", "World", nil].reject(&:blank?)

# ["Hello", "World"]

Create Array of Strings

You can use %W if you need interpolation

# Old
["Rails", "Tailwind", "HTML", "Stimulus"]

# New
%w[Rails Tailwind HTML Stimulus]

You can only use %w and %W on single words as the array splits on whitespace

Create Array of Symbols

You can use %I if you need interpolation

# Old
[:new, :edit, :create, :update, :destroy]

# New
%i[new edit create update destroy]

You can only use %i and %I on single words as the array splits on whitespace

Calculate Sum of Values

[10, 10, 20].sum

Do Block

Post.all.each do |post|

Single Line Do Blocks

Post.all.each{ |post| }

Create a Method

def do_something

Create Method with Arguments

def do_something(name)

Create Method with Default Argument

def do_something(name = "User")

Create a Class

class Person

Create a Class that Accepts Arguments

class Person
	attr_reader :name

	def initialize(name:)
		@name = name

person = "Simon")

If/Else Statement

number = 20

if number > 10
	"Greater than 10"
	"Less than 10"

If/Elsif/Else Statement

number = 20

if number > 15
	"Greater than 15"
elsif number > 10
	"Greater than 10"
	"Less than 10"

Case/Switch Statement

number = 20

case number
when > 15
	"Greater than 15"
when > 10
	"Greater than 10"
	"Less than 10"

PG Search (Gem)

bundle add pg_search

Search Against Single Attribute on Model

include PgSearch::Model

pg_search_scope :search, against: :title

Search Against Multiple Attributes on Model

include PgSearch::Model

pg_search_scope :search, against: %i[title subtitle body]

Search Against Action Text Data

Replace body with the name of your column using Action Text

include PgSearch::Model

pg_search_scope :search, associated_against: { rich_text_body: :body }

Web Console (Gem)

Installed by default

View Interactive Rails Console in the Browser

class ProjectsController < ApplicationController
	def index
		@projects = Project.all


Or you can use it in ERB like so.

<h1>All Projects</h1>

<%= console %>

Friendly ID (Gem)

bundle add friendly_id

Use Single Attribute

class Project < ApplicationRecord
	extend FriendlyId
	friendly_id :title, use: [:slugged, :finders]

Fallback to Multiple Attributes

If title is not unique fallback to title-company

class Project < ApplicationRecord
	extend FriendlyId
	friendly_id :slug_candidates, use: [:slugged, :finders]

	def slug_candidates
		[:title, [:title, :company]]

Better Find

This removes the need to use friendly.find

use: [:slugged, :finders]

Stripe (Gem)

bundle add stripe

Setup Stripe


Rails.configuration.stripe = {
  publishable_key: ENV["STRIPE_PUBLISHABLE_KEY"],
  secret_key: ENV["STRIPE_SECRET_KEY"],

Stripe.api_key = Rails.configuration.stripe[:secret_key]


<%= javascript_include_tag "" %>

Stripe Checkout


scope "checkout" do
	post "create", to: "checkout#create", as: :checkout_create
	get "cancel", to: "checkout#cancel", as: :checkout_cancel
	get "success", to: "checkout#success", as: :checkout_success


class CheckoutController < ApplicationController
  def create
    @session = Stripe::Checkout::Session.create(
      payment_method_types: %w[card],
      line_items: [
          name: params[:name],
          description: params[:description],
          amount: params[:amount],
          currency: "gbp",
          quantity: params[:quantity],
      success_url: checkout_success_url,
      cancel_url: checkout_cancel_url,

    respond_to do |format|

  def success; end

  def cancel; end


const stripe = Stripe("<%= ENV['STRIPE_PUBLISHABLE_KEY'] %>")

  sessionId: "<%= %>"


<%= form_with(url: checkout_create_path, local: false) do |form| %>
    <%= form.hidden_field :name, value: @product.title %>
    <%= form.hidden_field :description, value: @product.description %>
    <%= form.hidden_field :amount, value: @product.price * 100 %> <%= form.number_field :quantity, value: 1 %>
    <%= form.submit %>
<% end %>

Code Snippets

Rails Based Body Class

Set a unique class to the <body> tag, created from the current controller and action names

def page_class

And then in your layout file you can do this.

<body class="<%= page_class %>">

Better Email Link

Removes the need to pass the email twice when using HTML attributes

def email_to(email, **object)
  link_to(email, "mailto:#{email}", **object)

Better Phone Link

Removes the need to pass the email twice when using HTML attributes

def tel_to(number, **object)
  link_to(number, "tel:#{number}", **object)

As of Rails v6.1.3.1 there is a phone_to helper