Ruby on Rails
Updated: May 22, 2026Categories: Languages, Web, Frontend, Backend, Framework
Printed from:
Ruby on Rails Cheatsheet
1. Installation and Setup
Prerequisites
- Ruby (recommended version: 3.2+ for Rails 8)
- RubyGems
- Bundler
Installation
Bash
12345678910111213# Install Rails
gem install rails
# Create a new Rails project (Rails 8 defaults)
rails new project_name
# Create a new Rails project with specific options
rails new project_name \
-d postgresql \
--skip-test \
--css=tailwind \
--javascript=esbuild
Ruby Version Management
Bash
123456789101112# Using rbenv
rbenv install 3.3.0
rbenv global 3.3.0
# Using asdf
asdf install ruby 3.3.0
asdf global ruby 3.3.0
# Using RVM
rvm install 3.3.0
rvm use 3.3.0
2. Project Structure
my_rails_app/
├── app/
│ ├── controllers/
│ ├── models/
│ ├── views/
│ ├── helpers/
│ ├── assets/
│ ├── javascript/
│ ├── jobs/
│ ├── mailers/
│ └── channels/
├── config/
│ ├── routes.rb
│ ├── database.yml
│ ├── application.rb
│ └── credentials.yml.enc
├── db/
│ └── migrate/
├── lib/
├── log/
├── public/
├── storage/
├── test/ or spec/
└── vendor/
3. Routing
Basic Routes
Ruby
12345678910111213141516171819202122232425# config/routes.rb
Rails.application.routes.draw do
# RESTful routes for a resource
resources :users
# Custom routes
get '/about', to: 'pages#about'
# Nested routes
resources :posts do
resources :comments
end
# Namespaced routes
namespace :admin do
resources :users
end
# Root route
root 'pages#home'
# Health check endpoint (Rails 7.1+ default)
get 'up' => 'rails/health#show', as: :rails_health_check
end
Route Helpers
Ruby
12345# Generated route helpers
users_path # => '/users'
new_user_path # => '/users/new'
edit_user_path(user) # => '/users/1/edit'
4. Controllers
Basic Controller
Ruby
1234567891011121314151617181920212223242526272829303132333435363738class UsersController < ApplicationController
# Filter/before action
before_action :set_user, only: [:show, :edit, :update, :destroy]
def index
@users = User.all
end
def show
end
def new
@user = User.new
end
def create
@user = User.new(user_params)
if @user.save
redirect_to @user, notice: 'User created!'
else
# Rails 7+ requires explicit status code with render on failed create/update
render :new, status: :unprocessable_entity
end
end
private
def set_user
@user = User.find(params[:id])
end
def user_params
params.expect(user: [:name, :email])
# Or the classic form:
# params.require(:user).permit(:name, :email)
end
end
Strong Parameters
Ruby
1234567891011121314# Rails 8 introduced params.expect for safer parameter handling
def user_params
params.expect(user: [:name, :email, addresses: [[:street, :city]]])
end
# Classic form (still supported)
def user_params
params.require(:user).permit(
:name,
:email,
addresses: [:street, :city]
)
end
5. Views
ERB Basics
erb
12345678910111213141516<%# Embedded Ruby (ERB) %> <h1>Welcome, <%= @user.name %></h1> <%# Conditional %> <% if @users.any? %> <ul> <% @users.each do |user| %> <li><%= user.name %></li> <% end %> </ul> <% end %> <%# Partials %> <%= render 'shared/header' %> <%= render partial: 'user', collection: @users %>
View Helpers
Ruby
123456# In view or helper
link_to 'Edit', edit_user_path(user)
image_tag 'logo.png'
number_to_currency(price)
button_to 'Delete', user_path(user), method: :delete
Hotwire (Turbo + Stimulus)
erb
123456789<%# Turbo Frame %> <%= turbo_frame_tag "user_#{user.id}" do %> <%= user.name %> <% end %> <%# Turbo Stream broadcasts from models %> <%# In model: %> <%# broadcasts_to ->(post) { [post.user, :posts] } %>
6. Models
ActiveRecord Model
Ruby
1234567891011121314151617181920212223242526272829303132333435363738class User < ApplicationRecord
# Validations
validates :email,
presence: true,
uniqueness: true,
format: { with: URI::MailTo::EMAIL_REGEXP }
# Normalization (Rails 7.1+)
normalizes :email, with: ->(email) { email.strip.downcase }
# Encrypted attributes (Rails 7+)
encrypts :ssn, deterministic: true
# Associations
has_many :posts, dependent: :destroy
belongs_to :organization, optional: true
# Secure password (requires bcrypt)
has_secure_password
# Scopes
scope :active, -> { where(active: true) }
scope :recent, ->(days) { where('created_at >= ?', days.days.ago) }
# Enum (Rails 7+ keyword syntax)
enum :status, { pending: 0, active: 1, archived: 2 }
# Class method
def self.find_by_email(email)
find_by(email: email)
end
# Instance method
def full_name
"#{first_name} #{last_name}"
end
end
Query Methods
Ruby
12345678910# Finding records
User.find(1) # Find by ID
User.find_by(email: 'x@y.com') # Find first matching record
User.where(active: true) # Find all matching records
User.order(created_at: :desc) # Ordered results
User.limit(10) # Limit results
User.includes(:posts) # Eager loading
User.in_order_of(:status, %w[pending active]) # Rails 7+
User.with(active_users: User.where(active: true)) # CTEs (Rails 7.1+)
7. Database Operations
Migrations
Bash
123456789# Generate migration
rails generate migration CreateUsers name:string email:string
# Run migrations
rails db:migrate
rails db:rollback
rails db:reset
rails db:prepare # Create + load schema + seed if needed
Ruby
1234567891011# Migration example
class CreateUsers < ActiveRecord::Migration[8.0]
def change
create_table :users do |t|
t.string :name
t.string :email, index: { unique: true }
t.timestamps
end
end
end
Multiple Databases (Rails 6+)
YAML
12345678# config/database.yml
production:
primary:
database: my_primary_db
primary_replica:
database: my_primary_db
replica: true
Seeds
Ruby
12345678# db/seeds.rb
5.times do
User.create!(
name: Faker::Name.name,
email: Faker::Internet.email
)
end
Bash
12rails db:seed
8. Authentication & Authorization
Built-in Authentication Generator (Rails 8)
Bash
12345678# Rails 8 ships a built-in authentication generator
rails generate authentication
# This generates:
# - Session, User, and PasswordsController
# - User model with has_secure_password
# - Migrations for users and sessions
Devise Gem (Alternative)
Ruby
12345678910# Gemfile
gem 'devise'
# Generate configuration
rails generate devise:install
rails generate devise User
# In application controller
before_action :authenticate_user!
Pundit for Authorization
Ruby
12345678910111213# Gemfile
gem 'pundit'
# Generate policy
rails generate pundit:policy User
# Example policy
class UserPolicy < ApplicationPolicy
def update?
user.admin? || record == user
end
end
9. Testing
RSpec Setup
Ruby
12345# Gemfile
group :development, :test do
gem 'rspec-rails'
end
Bash
123# Generate specs
rails generate rspec:install
Ruby
12345678# Model spec example
RSpec.describe User, type: :model do
it "is valid with valid attributes" do
user = User.new(name: "John", email: "john@example.com")
expect(user).to be_valid
end
end
System Tests (Built-in)
Ruby
12345678910# test/system/users_test.rb
require "application_system_test_case"
class UsersTest < ApplicationSystemTestCase
test "visiting the index" do
visit users_url
assert_selector "h1", text: "Users"
end
end
Parallel Testing (Rails 6+)
Ruby
12345# test/test_helper.rb
class ActiveSupport::TestCase
parallelize(workers: :number_of_processors)
end
10. Common Commands
Bash
1234567891011121314151617181920# Rails Commands
rails new project_name
rails server # Start server (alias: rails s)
rails console # Interactive console (alias: rails c)
rails generate # Generate models, controllers (alias: rails g)
rails destroy # Remove generated resources
rails routes # List routes
rails dbconsole # Database console
rails credentials:edit # Edit encrypted credentials
# Bundler
bundle install # Install dependencies
bundle update # Update gems
bundle exec <cmd> # Run command in bundle context
# Testing
rails test
rails test:system
rspec # For RSpec
11. Configuration
Environment Configuration
Ruby
1234567891011121314151617# config/environments/development.rb
Rails.application.configure do
config.cache_classes = false
config.active_record.verbose_query_logs = true
end
# config/application.rb
module MyApp
class Application < Rails::Application
config.load_defaults 8.0
config.time_zone = 'Eastern Time (US & Canada)'
config.generators do |g|
g.test_framework :rspec
end
end
end
Encrypted Credentials
Bash
1234# Edit credentials (multi-environment support since Rails 6)
rails credentials:edit
rails credentials:edit --environment production
Ruby
123# Access in app
Rails.application.credentials.dig(:aws, :access_key_id)
12. Background Jobs & Async
Active Job
Ruby
1234567891011class NotificationJob < ApplicationJob
queue_as :default
def perform(user)
UserMailer.welcome(user).deliver_now
end
end
# Enqueue
NotificationJob.perform_later(user)
Solid Queue (Rails 8 default)
Rails 8 ships with Solid Queue as the default Active Job backend, backed by your database — no Redis required.
Ruby
123# config/application.rb
config.active_job.queue_adapter = :solid_queue
13. The Solid Trifecta (Rails 8)
Rails 8 introduces database-backed adapters that eliminate the need for Redis/Memcached in many setups:
- Solid Queue — Active Job backend
- Solid Cache — Rails.cache backend
- Solid Cable — Action Cable backend
Ruby
12345# config/environments/production.rb
config.cache_store = :solid_cache_store
config.active_job.queue_adapter = :solid_queue
config.action_cable.adapter = :solid_cable
14. Deployment with Kamal (Rails 8 default)
Rails 8 ships with Kamal for zero-downtime container deployments.
Bash
123456789# Set up Kamal
bin/kamal setup
# Deploy
bin/kamal deploy
# View logs
bin/kamal app logs
YAML
12345678910# config/deploy.yml
service: my_app
image: user/my_app
servers:
- 192.168.0.1
registry:
username: user
password:
- KAMAL_REGISTRY_PASSWORD
15. Frontend Stack
Default Frontend (Rails 7+)
- Importmap (default): pin JavaScript modules without bundling
- Hotwire: Turbo + Stimulus for SPA-like experiences without much JS
- Propshaft: modern asset pipeline replacing Sprockets (Rails 8 default)
Bash
12345678# Pin a JavaScript package
bin/importmap pin react
# Optional bundlers
rails new myapp --javascript=esbuild
rails new myapp --javascript=bun
rails new myapp --css=tailwind
16. Common Patterns and Best Practices
- Keep controllers skinny, models fat — extract to service objects when needed
- Use service objects or Interactors for complex business logic
- Implement thin views with helpers and ViewComponents
- Use concerns for shared behavior (sparingly)
- Follow RESTful conventions
- Use background jobs (Active Job + Solid Queue) for time-consuming tasks
- Implement caching strategies (fragment caching, Solid Cache)
- Use encrypted credentials and environment variables for configuration
- Prefer
params.expectoverparams.require.permitin Rails 8
17. Debugging and Troubleshooting
Ruby
123456789# Debugging (debug gem replaces byebug since Rails 7)
debugger # Breakpoint with debug gem
Rails.logger.debug # Logging
# Common Issues
# - N+1 queries: Use includes / eager_load (catch with bullet gem)
# - Slow queries: Add database indexes
# - Memory leaks: Profile with rack-mini-profiler or memory_profiler
Recommended Gems
devise: Authentication (or use Rails 8's built-in generator)pundit/action_policy: Authorizationsidekiq/good_job: Background jobs (Solid Queue is built in)rspec-rails: Testingfactory_bot_rails: Test data generationfaker: Fake data generationrubocop-rails-omakase: Rails 8's default style configurationbullet: N+1 query detectionbrakeman: Static security analysis (included by default in Rails 8)view_component: Reusable view componentsdry-rbfamily: Functional building blocks
Pro Tips:
- Keep dependencies updated (Rails 8 requires Ruby 3.2+)
- Monitor performance with tools like Skylight, Scout, or AppSignal
- Write comprehensive tests, including system tests
- Follow the Ruby and Rails style guides (
rubocop-rails-omakase) - Use environment-specific configurations
- Lean on Hotwire before reaching for a separate SPA framework
- Run
bin/brakemanandbin/rubocopregularly
Continue Learning
Discover more cheatsheets to boost your productivity