Skip to content

Ruby on Rails Cheat Sheet

Overview

Ruby on Rails (often just “Rails”) is a full-stack web application framework written in Ruby that follows the Model-View-Controller (MVC) architectural pattern. Created by David Heinemeier Hansson in 2004, Rails emphasizes convention over configuration (CoC) and the DRY (Don’t Repeat Yourself) principle, enabling rapid application development with sensible defaults. It includes everything needed to build database-backed web applications: an ORM (Active Record), routing, templating, asset pipeline, and testing framework.

Rails powers some of the world’s largest websites and applications including Shopify, GitHub, Basecamp, and Airbnb. The framework has continuously evolved through major versions, with Rails 7+ introducing Hotwire (Turbo + Stimulus) for building modern interactive applications without heavy JavaScript frameworks. Rails also includes Action Cable for WebSocket support, Active Job for background processing, and Active Storage for file uploads.

Installation

Setup

# Install Ruby (3.1+ recommended)
rbenv install 3.3.0
rbenv global 3.3.0

# Install Rails
gem install rails

# Create new application
rails new myapp
rails new myapp --database=postgresql
rails new myapp --api                    # API-only mode
rails new myapp --css=tailwind           # With Tailwind CSS
rails new myapp --skip-test --skip-jbuilder

# Setup and run
cd myapp
bin/setup
bin/rails server

Project Structure

PathDescription
app/models/Active Record models
app/controllers/Action controllers
app/views/ERB/HTML templates
app/helpers/View helper methods
app/jobs/Background jobs
app/mailers/Email handlers
app/channels/WebSocket channels
config/routes.rbURL routing
config/database.ymlDatabase configuration
db/migrate/Database migrations
db/schema.rbCurrent schema snapshot
test/ or spec/Test files
GemfileRuby dependencies

Generators

CommandDescription
rails g model User name:string email:stringGenerate model + migration
rails g controller Users index showGenerate controller + views
rails g scaffold Post title:string body:textFull CRUD resource
rails g migration AddAgeToUsers age:integerGenerate migration
rails g mailer UserMailer welcomeGenerate mailer
rails g job ProcessPaymentGenerate background job
rails g channel ChatGenerate WebSocket channel
rails destroy model UserRemove generated files

Routing

# config/routes.rb
Rails.application.routes.draw do
  root "pages#home"

  resources :users do
    resources :posts, only: [:index, :create]
    member do
      post :activate
    end
    collection do
      get :search
    end
  end

  namespace :admin do
    resources :dashboard, only: [:index]
  end

  scope "/api/v1", module: "api/v1" do
    resources :articles, only: [:index, :show]
  end

  get "about", to: "pages#about"
  get "health", to: proc { [200, {}, ["OK"]] }
end
# View all routes
bin/rails routes
bin/rails routes -g users  # Grep routes

Active Record

Models

class User < ApplicationRecord
  # Associations
  has_many :posts, dependent: :destroy
  has_many :comments, through: :posts
  has_one :profile
  belongs_to :organization, optional: true

  # Validations
  validates :name, presence: true, length: { minimum: 2, maximum: 100 }
  validates :email, presence: true, uniqueness: { case_sensitive: false },
                    format: { with: URI::MailTo::EMAIL_REGEXP }
  validates :age, numericality: { greater_than: 0 }, allow_nil: true

  # Callbacks
  before_save :normalize_email
  after_create :send_welcome_email

  # Scopes
  scope :active, -> { where(active: true) }
  scope :recent, -> { order(created_at: :desc).limit(10) }
  scope :adults, -> { where("age >= ?", 18) }

  # Enums
  enum :role, { user: 0, moderator: 1, admin: 2 }

  private

  def normalize_email
    self.email = email.downcase.strip
  end

  def send_welcome_email
    UserMailer.welcome(self).deliver_later
  end
end

Queries

# Finding records
User.find(1)                      # By ID (raises if not found)
User.find_by(email: "a@b.com")   # First match (returns nil)
User.where(active: true)          # Collection
User.where("age > ?", 18)         # SQL condition
User.where(role: [:admin, :moderator])

# Chaining
User.active.recent.where(role: :admin).limit(5)

# Aggregations
User.count
User.average(:age)
User.group(:role).count

# Eager loading (avoid N+1)
User.includes(:posts).where(posts: { published: true })
User.preload(:comments)

# Raw SQL
User.find_by_sql("SELECT * FROM users WHERE age > 18")

# Batching
User.find_each(batch_size: 1000) { |user| process(user) }

Migrations

bin/rails db:migrate
bin/rails db:rollback
bin/rails db:migrate:status
bin/rails db:seed
bin/rails db:reset          # Drop + create + migrate + seed
class CreateUsers < ActiveRecord::Migration[7.1]
  def change
    create_table :users do |t|
      t.string :name, null: false
      t.string :email, null: false
      t.integer :age
      t.references :organization, foreign_key: true
      t.timestamps
    end

    add_index :users, :email, unique: true
    add_index :users, [:name, :organization_id]
  end
end

Controllers

class UsersController < ApplicationController
  before_action :authenticate_user!
  before_action :set_user, only: [:show, :edit, :update, :destroy]

  def index
    @users = User.active.page(params[:page])
  end

  def create
    @user = User.new(user_params)
    if @user.save
      redirect_to @user, notice: "User created."
    else
      render :new, status: :unprocessable_entity
    end
  end

  def update
    if @user.update(user_params)
      redirect_to @user, notice: "User updated."
    else
      render :edit, status: :unprocessable_entity
    end
  end

  def destroy
    @user.destroy
    redirect_to users_path, notice: "User deleted.", status: :see_other
  end

  private

  def set_user
    @user = User.find(params[:id])
  end

  def user_params
    params.require(:user).permit(:name, :email, :age, :role)
  end
end

Configuration

Database Configuration

# config/database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

development:
  <<: *default
  database: myapp_development

test:
  <<: *default
  database: myapp_test

production:
  <<: *default
  url: <%= ENV["DATABASE_URL"] %>

Environment Configuration

# config/environments/production.rb
Rails.application.configure do
  config.cache_classes = true
  config.eager_load = true
  config.consider_all_requests_local = false
  config.action_controller.perform_caching = true
  config.active_job.queue_adapter = :sidekiq
  config.force_ssl = true
  config.log_level = :info
end

Advanced Usage

Background Jobs

class ProcessPaymentJob < ApplicationJob
  queue_as :critical
  retry_on Stripe::RateLimitError, wait: :polynomially_longer, attempts: 5
  discard_on ActiveRecord::RecordNotFound

  def perform(order_id)
    order = Order.find(order_id)
    PaymentService.charge(order)
  end
end

# Enqueue
ProcessPaymentJob.perform_later(order.id)
ProcessPaymentJob.set(wait: 1.hour).perform_later(order.id)

Action Mailer

class UserMailer < ApplicationMailer
  def welcome(user)
    @user = user
    mail(to: @user.email, subject: "Welcome!")
  end
end

# Send
UserMailer.welcome(user).deliver_later

Hotwire (Turbo + Stimulus)

<%# Turbo Frame %>
<%= turbo_frame_tag "user_#{@user.id}" do %>
  <%= render @user %>
<% end %>

<%# Turbo Stream %>
<%= turbo_stream.append "messages", partial: "messages/message", locals: { message: @message } %>

Testing

# test/models/user_test.rb
class UserTest < ActiveSupport::TestCase
  test "should not save user without name" do
    user = User.new(email: "test@example.com")
    assert_not user.save
  end
end

# test/controllers/users_controller_test.rb
class UsersControllerTest < ActionDispatch::IntegrationTest
  test "should get index" do
    get users_url
    assert_response :success
  end
end

Troubleshooting

ProblemSolution
PendingMigrationErrorRun bin/rails db:migrate
N+1 query warningsUse includes(:association) for eager loading
ActionController::ParameterMissingCheck params.require and permit in controller
Asset pipeline errorsRun bin/rails assets:precompile
Couldn't find User with id=Check record exists; use find_by for nil return
Slow queriesAdd database indexes; check with EXPLAIN ANALYZE
Memory issues in productionTune WEB_CONCURRENCY and RAILS_MAX_THREADS
CSRF token errorsEnsure <%= csrf_meta_tags %> in layout