LearnThatStack Ace your next interview

Ruby.
Cheatsheet.

Quick reference for Ruby - sectioned for fast scanning. Skim the part you're shaky on, walk in confident.

Backend Development 23-section reference ~16 min read

Overview

Ruby Cheat Sheet

Introduction

Ruby is a dynamic, object-oriented programming language known for its elegant syntax and developer-friendly philosophy. Combined with Rails framework, it powers major platforms like GitHub, Shopify, and Airbnb. This cheatsheet covers essential Ruby concepts, Rails patterns, and backend development topics frequently tested in technical interviews.

Key Strengths: Developer productivity, readable syntax, rich ecosystem, convention over configuration (Rails), strong testing culture, and excellent metaprogramming capabilities.

Interview Focus Areas: Object-oriented design, metaprogramming, Rails MVC patterns, ActiveRecord queries, performance optimization, testing, and system design with Ruby.

1. Ruby Basics

Variables & Constants

local_var = "local"        # Local variable
@instance_var = "instance" # Instance variable
@@class_var = "class"      # Class variable
$global_var = "global"     # Global variable
CONSTANT = "constant"      # Constant

Data Types

# Numbers
int = 42
float = 3.14
bignum = 9999999999999999999

# Strings
str = "Hello"
str = 'World'
str = %q(Single quote string)
str = %Q(Double quote string)

# Symbols (immutable strings)
:symbol_name

# Arrays
arr = [1, 2, 3]
arr = Array.new(3, 0)  # [0, 0, 0]

# Hashes
hash = { name: "John", age: 30 }
hash = { "key" => "value" }

# Ranges
(1..5)   # 1, 2, 3, 4, 5
(1...5)  # 1, 2, 3, 4

String Operations

"hello".upcase     # "HELLO"
"WORLD".downcase   # "world"
"ruby".capitalize  # "Ruby"
"  trim  ".strip   # "trim"
"hello".reverse    # "olleh"
"a,b,c".split(',') # ["a", "b", "c"]
["a","b"].join('-') # "a-b"

# String interpolation
name = "Ruby"
"Hello #{name}"    # "Hello Ruby"

# Multiline strings
str = <<~HEREDOC
  This is a
  multiline string
HEREDOC

Control Structures

# If/elsif/else
if x > 0
  "positive"
elsif x < 0
  "negative"
else
  "zero"
end

# Ternary
result = x > 0 ? "positive" : "non-positive"

# Unless
puts "negative" unless x > 0

# Case/when
case grade
when 90..100 then "A"
when 80..89 then "B"
else "C"
end

Loops

# While
while i < 5
  i += 1
end

# Until
until i >= 5
  i += 1
end

# For
for i in 1..5
  puts i
end

# Times
5.times { |i| puts i }

# Each
[1,2,3].each { |n| puts n }

# Map/collect
[1,2,3].map { |n| n * 2 }  # [2,4,6]

# Select/filter
[1,2,3,4].select { |n| n.even? }  # [2,4]

2. Methods & Blocks

Method Definition

# Basic method
def greet(name)
  "Hello #{name}"
end

# Default parameters
def greet(name = "World")
  "Hello #{name}"
end

# Variable arguments
def sum(*nums)
  nums.reduce(:+)
end

# Keyword arguments
def create_user(name:, age: 18)
  { name: name, age: age }
end

# Method with block
def with_timing
  start = Time.now
  yield
  Time.now - start
end

Blocks, Procs & Lambdas

# Block
[1,2,3].each { |n| puts n }
[1,2,3].each do |n|
  puts n
end

# Proc
my_proc = Proc.new { |x| x * 2 }
my_proc.call(5)  # 10

# Lambda
my_lambda = ->(x) { x * 2 }
my_lambda.call(5)  # 10

# Difference: Lambda checks argument count, Proc doesn't
# Lambda returns to method, Proc returns from method

3. Object-Oriented Programming

Classes & Objects

class Person
  attr_accessor :name  # getter & setter
  attr_reader :age    # getter only
  attr_writer :email   # setter only
  
  @@count = 0  # class variable
  
  def initialize(name, age)
    @name = name
    @age = age
    @@count += 1
  end
  
  def self.count  # class method
    @@count
  end
  
  def greet  # instance method
    "Hi, I'm #{@name}"
  end
  
  private
  
  def secret_method
    "private"
  end
end

person = Person.new("John", 30)

Inheritance

class Employee < Person
  def initialize(name, age, salary)
    super(name, age)  # call parent constructor
    @salary = salary
  end
  
  def greet
    super + " and I work here"
  end
end

Modules & Mixins

module Greetable
  def hello
    "Hello!"
  end
end

module Trackable
  def self.included(base)
    base.extend(ClassMethods)
  end
  
  module ClassMethods
    def track_method(name)
      # class method
    end
  end
end

class User
  include Greetable  # instance methods
  extend Trackable   # class methods
end

Method Visibility

class MyClass
  public    # default
  def public_method; end
  
  protected # accessible by subclasses
  def protected_method; end
  
  private   # only accessible within class
  def private_method; end
end

4. Advanced Ruby Features

Metaprogramming

# Define method dynamically
define_method :dynamic_method do |arg|
  "Dynamic #{arg}"
end

# Method missing
def method_missing(method, *args)
  "Method #{method} not found"
end

# Send method
object.send(:method_name, args)

# Eval
eval("1 + 1")  # 2

# Class eval
MyClass.class_eval do
  def new_method
    "added"
  end
end

Singleton Methods & Classes

# Singleton method
obj = Object.new
def obj.unique_method
  "only for this instance"
end

# Singleton class
class << obj
  def another_unique
    "also unique"
  end
end

Duck Typing

# If it walks like a duck and quacks like a duck...
def process(obj)
  obj.to_s  # works with any object that responds to to_s
end

5. Enumerable & Collections

Common Enumerable Methods

arr = [1, 2, 3, 4, 5]

# Iteration - O(n)
arr.each { |n| puts n }
arr.each_with_index { |n, i| puts "#{i}: #{n}" }

# Transformation - O(n)
arr.map { |n| n * 2 }      # [2,4,6,8,10]
arr.select { |n| n.odd? }  # [1,3,5]
arr.reject { |n| n.odd? }  # [2,4]
arr.reduce(:+)             # 15 - O(n)
arr.inject(0) { |sum, n| sum + n }  # 15

# Searching - O(n) worst case
arr.find { |n| n > 3 }     # 4 - stops at first match
arr.find_all { |n| n > 3 } # [4,5]
arr.any? { |n| n > 3 }     # true - stops at first
arr.all? { |n| n > 0 }     # true - may check all
arr.none? { |n| n < 0 }    # true - may check all

# Grouping & Sorting
[1,2,3,4].group_by { |n| n.odd? }  # O(n)
# {true=>[1,3], false=>[2,4]}
arr.sort                   # O(n log n)
arr.sort_by { |n| -n }     # O(n log n)

Array Methods

arr = [1, 2, 3]
arr.push(4)      # [1,2,3,4] - O(1)
arr << 5         # [1,2,3,4,5] - O(1)
arr.pop          # returns 5 - O(1)
arr.shift        # returns 1 - O(n)
arr.unshift(0)   # [0,2,3,4] - O(n)
arr.first(2)     # [0,2] - O(1)
arr.last(2)      # [3,4] - O(1)
arr.include?(3)  # true - O(n)
arr.flatten      # flattens nested - O(n)
arr.compact      # removes nil - O(n)
arr.uniq         # removes duplicates - O(n)

Hash Methods

hash = { a: 1, b: 2, c: 3 }
hash.keys        # [:a, :b, :c] - O(n)
hash.values      # [1, 2, 3] - O(n)
hash.each { |k, v| puts "#{k}: #{v}" }  # O(n)
hash.merge({ d: 4 })  # O(n+m)
hash.select { |k, v| v > 1 }  # O(n)
hash.fetch(:a, 0)  # O(1) average
hash.dig(:a)       # O(1) average
hash[:key] = val   # O(1) average
hash.delete(:key)  # O(1) average

6. Exception Handling

begin
  # risky code
  result = 10 / 0
rescue ZeroDivisionError => e
  puts "Error: #{e.message}"
rescue StandardError => e
  puts "General error: #{e.message}"
else
  puts "No errors"
ensure
  puts "Always runs"
end

# Inline rescue
value = risky_method rescue "default"

# Raising exceptions
raise "Error message"
raise ArgumentError, "Invalid argument"

# Custom exceptions
class CustomError < StandardError; end

7. File I/O & System

File Operations

# Reading
content = File.read("file.txt")
lines = File.readlines("file.txt")

File.open("file.txt", "r") do |file|
  file.each_line { |line| puts line }
end

# Writing
File.write("file.txt", "content")
File.open("file.txt", "w") { |f| f.write("text") }
File.open("file.txt", "a") { |f| f.puts("append") }

# File info
File.exist?("file.txt")
File.size("file.txt")
File.directory?("path")

Directory Operations

Dir.pwd                 # current directory
Dir.entries(".")        # list files
Dir.glob("*.rb")        # pattern matching
Dir.mkdir("new_dir")    # create directory

8. Regular Expressions

# Basic patterns
/pattern/           # basic regex
/pattern/i          # case insensitive
/pattern/m          # multiline

# Common patterns
/\d+/              # digits
/\w+/              # word characters
/\s+/              # whitespace
/^start/           # beginning of line
/end$/             # end of line
/a.b/              # any character
/a.*b/             # zero or more
/a.+b/             # one or more
/a.?b/             # zero or one
/[aeiou]/          # character class
/(group)/          # capturing group

# Usage
"test".match?(/es/)      # true
"test".scan(/[aeiou]/)   # ["e"]
"test".gsub(/e/, "3")    # "t3st"
"a,b,c".split(/,/)       # ["a", "b", "c"]

9. Rails Fundamentals

MVC Architecture

# Model - Business logic and data
class User < ApplicationRecord
  has_many :posts
  validates :email, presence: true
end

# View - Presentation layer (ERB templates)
<%= @user.name %>

# Controller - Request handling
class UsersController < ApplicationController
  def show
    @user = User.find(params[:id])
  end
end

ActiveRecord Basics

# Model
class User < ApplicationRecord
  has_many :posts
  belongs_to :company
  has_one :profile
  has_and_belongs_to_many :groups
  
  validates :email, presence: true, uniqueness: true
  validates :age, numericality: { greater_than: 18 }
  
  scope :active, -> { where(active: true) }
  scope :recent, -> { order(created_at: :desc) }
  
  before_save :normalize_email
  after_create :send_welcome_email
  
  private
  
  def normalize_email
    self.email = email.downcase.strip
  end
end

# Queries - Performance considerations
User.find(1)                              # O(1) with index
User.find_by(email: "test@example.com")  # O(1) with index
User.where(active: true)                  # O(n) without index
User.where("age > ?", 18)                # O(n) or O(log n) with index
User.joins(:posts).where(posts: { published: true })
User.includes(:posts)  # Prevents N+1 queries
User.limit(10).offset(20)                # Pagination
User.order(created_at: :desc)            # O(n log n) without index
User.group(:status).count                 # O(n)

Migrations

class CreateUsers < ActiveRecord::Migration[7.0]
  def change
    create_table :users do |t|
      t.string :name, null: false
      t.string :email, index: { unique: true }
      t.integer :age
      t.references :company, foreign_key: true
      t.timestamps
    end
  end
end

Controller Patterns

class UsersController < ApplicationController
  before_action :set_user, only: [:show, :edit, :update, :destroy]
  
  def index
    @users = User.page(params[:page])
  end
  
  def create
    @user = User.new(user_params)
    if @user.save
      redirect_to @user
    else
      render :new
    end
  end
  
  private
  
  def set_user
    @user = User.find(params[:id])
  end
  
  def user_params
    params.require(:user).permit(:name, :email)
  end
end

Service Objects Pattern

class UserRegistrationService
  def initialize(params)
    @params = params
  end
  
  def call
    ActiveRecord::Base.transaction do
      user = User.create!(@params)
      send_welcome_email(user)
      create_default_settings(user)
      user
    end
  rescue => e
    OpenStruct.new(success?: false, error: e.message)
  end
  
  private
  
  def send_welcome_email(user)
    UserMailer.welcome(user).deliver_later
  end
  
  def create_default_settings(user)
    user.create_settings(theme: 'light')
  end
end

# Usage in controller
def create
  service = UserRegistrationService.new(user_params)
  @user = service.call
  
  if @user.persisted?
    redirect_to @user
  else
    render :new
  end
end

Concerns & Modules

# app/models/concerns/trackable.rb
module Trackable
  extend ActiveSupport::Concern
  
  included do
    has_many :activities, as: :trackable
    after_create :log_creation
  end
  
  def track_activity(action)
    activities.create(action: action, user: Current.user)
  end
  
  private
  
  def log_creation
    track_activity('created')
  end
end

# Usage
class Post < ApplicationRecord
  include Trackable
end

Callbacks & Validations

class User < ApplicationRecord
  # Callbacks
  before_validation :normalize_email
  after_create :send_welcome_email
  after_update :notify_profile_update, if: :profile_changed?
  
  # Validations
  validates :email, presence: true, uniqueness: true, format: URI::MailTo::EMAIL_REGEXP
  validates :age, numericality: { greater_than_or_equal_to: 18 }
  validates :username, length: { in: 3..20 }
  validate :email_not_blacklisted
  
  private
  
  def email_not_blacklisted
    errors.add(:email, "is not allowed") if EmailBlacklist.exists?(email: email)
  end
end

10. Testing with RSpec

# Model spec
describe User do
  it "is valid with valid attributes" do
    user = User.new(name: "John", email: "john@example.com")
    expect(user).to be_valid
  end
  
  it "is invalid without email" do
    user = User.new(name: "John")
    expect(user).not_to be_valid
  end
end

# Controller spec
describe UsersController do
  describe "GET #index" do
    it "returns success" do
      get :index
      expect(response).to have_http_status(:success)
    end
  end
end

# Common matchers
expect(value).to eq(5)
expect(value).to be_truthy
expect(array).to include(1)
expect { code }.to raise_error
expect { code }.to change { User.count }.by(1)

# Factory Bot
FactoryBot.define do
  factory :user do
    name { Faker::Name.name }
    email { Faker::Internet.email }
    age { 25 }
    
    trait :admin do
      role { 'admin' }
    end
  end
end

# Usage
user = create(:user)
admin = create(:user, :admin)
users = create_list(:user, 3)

11. Concurrency & Threading

Global Interpreter Lock (GIL/GVL)

# Ruby MRI has a GIL that prevents true parallel execution of Ruby code
# But I/O operations release the GIL, allowing concurrency

# CPU-bound tasks don't benefit from threads in MRI
threads = 4.times.map do
  Thread.new { calculate_prime(1000000) }  # Won't run in parallel
end
threads.each(&:join)

# I/O-bound tasks DO benefit from threads
threads = urls.map do |url|
  Thread.new { fetch_data(url) }  # Can run concurrently
end
results = threads.map(&:value)

Thread Safety

# Race condition example - NOT thread-safe
@counter = 0
threads = 10.times.map do
  Thread.new { 1000.times { @counter += 1 } }
end
threads.each(&:join)
# @counter might not be 10000!

# Thread-safe with Mutex
@counter = 0
@mutex = Mutex.new
threads = 10.times.map do
  Thread.new do
    1000.times do
      @mutex.synchronize { @counter += 1 }
    end
  end
end
threads.each(&:join)
# @counter will be 10000

# Thread-safe data structures
require 'concurrent'
safe_array = Concurrent::Array.new
safe_hash = Concurrent::Hash.new

Concurrent Ruby Patterns

# Future for async operations
require 'concurrent'
future = Concurrent::Future.execute { expensive_calculation }
# Do other work...
result = future.value  # Blocks until complete

# Thread pool
pool = Concurrent::FixedThreadPool.new(5)
pool.post { perform_task }
pool.shutdown
pool.wait_for_termination

# Actor model
class Counter
  include Concurrent::Async
  
  def increment
    @count = (@count || 0) + 1
  end
end

counter = Counter.new
counter.async.increment

12. Memory Management & Garbage Collection

Ruby Memory Model

# Object allocation
ObjectSpace.count_objects  # See object counts
ObjectSpace.memsize_of(obj)  # Memory size of object

# Garbage collection
GC.start  # Force garbage collection
GC.stat   # GC statistics
GC.disable/GC.enable  # Control GC

# Memory profiling
require 'objspace'
ObjectSpace.trace_object_allocations_start
# ... code to profile ...
ObjectSpace.trace_object_allocations_stop

Common Memory Issues

# Memory leak - holding references
class Leaky
  @@all_instances = []
  
  def initialize
    @@all_instances << self  # Never released!
  end
end

# Fixed version
class NonLeaky
  def self.track(instance)
    @instances ||= []
    @instances << WeakRef.new(instance)
  end
end

# String allocation optimization
# Bad - creates new string each time
def bad_method
  "status: " + status  # New string object
end

# Good - reuses frozen string
FROZEN = "status: ".freeze
def good_method
  "#{FROZEN}#{status}"  # Less allocation
end

13. Performance & Optimization

Benchmarking

require 'benchmark'

Benchmark.bm do |x|
  x.report("method1") { 1000.times { method1 } }
  x.report("method2") { 1000.times { method2 } }
end

Memoization

def expensive_calculation
  @result ||= begin
    # expensive operation
    sleep(2)
    42
  end
end

Database Optimization

# N+1 query problem - BAD
users.each { |user| puts user.posts.count }

# Solution - GOOD
users.includes(:posts).each { |user| puts user.posts.size }

# Use pluck for single columns
User.pluck(:email)  # returns array of emails

# Use select for limiting columns
User.select(:id, :name)

# Batch processing
User.find_each(batch_size: 1000) do |user|
  # process user
end

12. Common Interview Patterns

Singleton Pattern

class Singleton
  def self.instance
    @instance ||= new
  end
  
  private_class_method :new
end

Factory Pattern

class AnimalFactory
  def self.create(type)
    case type
    when :dog then Dog.new
    when :cat then Cat.new
    end
  end
end

Observer Pattern

module Observable
  def observers
    @observers ||= []
  end
  
  def add_observer(observer)
    observers << observer
  end
  
  def notify_observers
    observers.each { |obs| obs.update(self) }
  end
end

Decorator Pattern

class Coffee
  def cost; 2; end
  def description; "Coffee"; end
end

class MilkDecorator
  def initialize(coffee)
    @coffee = coffee
  end
  
  def cost
    @coffee.cost + 0.5
  end
  
  def description
    @coffee.description + " with milk"
  end
end

13. Algorithms & Complexity

Common Algorithm Patterns

Fibonacci Sequence

# Recursive - O(2^n) time, O(n) space
def fibonacci(n)
  return n if n <= 1
  fibonacci(n - 1) + fibonacci(n - 2)
end

# Memoized - O(n) time, O(n) space
def fibonacci_memo(n, memo = {})
  return n if n <= 1
  memo[n] ||= fibonacci_memo(n-1, memo) + fibonacci_memo(n-2, memo)
end

# Iterative - O(n) time, O(1) space
def fibonacci_iter(n)
  return n if n <= 1
  prev, curr = 0, 1
  (n - 1).times { prev, curr = curr, prev + curr }
  curr
end

String Manipulation

# Palindrome check - O(n) time, O(n) space
def palindrome?(str)
  clean = str.downcase.gsub(/\W/, '')
  clean == clean.reverse
end

# Anagram check - O(n log n) time
def anagram?(str1, str2)
  str1.chars.sort == str2.chars.sort
end

Array Operations

# Flatten nested array - O(n) time for n total elements
def flatten_array(arr)
  arr.reduce([]) do |flat, element|
    flat + (element.is_a?(Array) ? flatten_array(element) : [element])
  end
end

# Find duplicates - O(n) time, O(n) space
def find_duplicates(arr)
  seen = Set.new
  arr.select { |item| !seen.add?(item) }
end

# Two sum problem - O(n) time, O(n) space
def two_sum(nums, target)
  seen = {}
  nums.each_with_index do |num, i|
    complement = target - num
    return [seen[complement], i] if seen.key?(complement)
    seen[num] = i
  end
  nil
end

14. API Development

RESTful Design

# Rails RESTful routes
resources :users do
  resources :posts  # Nested resources
end
# Creates: GET /users, POST /users, GET /users/:id, etc.

# API versioning
namespace :api do
  namespace :v1 do
    resources :users
  end
end

# Custom routes
get 'search', to: 'products#search'
post 'users/:id/activate', to: 'users#activate'

JSON Serialization

# Active Model Serializers
class UserSerializer < ActiveModel::Serializer
  attributes :id, :name, :email
  has_many :posts
  
  def email
    object.admin? ? object.email : nil
  end
end

# Jbuilder
# views/users/show.json.jbuilder
json.user do
  json.id @user.id
  json.name @user.name
  json.posts @user.posts, :id, :title
end

# Manual serialization
def as_json(options = {})
  super(options.merge(
    only: [:id, :name],
    include: { posts: { only: [:id, :title] } }
  ))
end

API Controllers

class Api::V1::BaseController < ApplicationController
  skip_before_action :verify_authenticity_token
  before_action :authenticate_api_user!
  
  rescue_from ActiveRecord::RecordNotFound, with: :not_found
  rescue_from ActiveRecord::RecordInvalid, with: :unprocessable
  
  private
  
  def authenticate_api_user!
    token = request.headers['Authorization']&.split(' ')&.last
    @current_user = User.find_by_token(token)
    render_unauthorized unless @current_user
  end
  
  def render_unauthorized
    render json: { error: 'Unauthorized' }, status: :unauthorized
  end
  
  def not_found
    render json: { error: 'Not found' }, status: :not_found
  end
end

Authentication Patterns

# JWT authentication
class JsonWebToken
  SECRET_KEY = Rails.application.secrets.secret_key_base
  
  def self.encode(payload, exp = 24.hours.from_now)
    payload[:exp] = exp.to_i
    JWT.encode(payload, SECRET_KEY)
  end
  
  def self.decode(token)
    decoded = JWT.decode(token, SECRET_KEY)[0]
    HashWithIndifferentAccess.new(decoded)
  rescue JWT::DecodeError
    nil
  end
end

# API key authentication
class ApiKey < ApplicationRecord
  before_create :generate_key
  
  private
  
  def generate_key
    self.key = SecureRandom.hex(32)
  end
end

15. Background Jobs & Caching

Sidekiq Patterns

# Job definition
class EmailWorker
  include Sidekiq::Worker
  sidekiq_options queue: 'mailers', retry: 3
  
  def perform(user_id)
    user = User.find(user_id)
    UserMailer.welcome(user).deliver_now
  end
end

# Enqueue jobs
EmailWorker.perform_async(user.id)
EmailWorker.perform_in(5.minutes, user.id)
EmailWorker.perform_at(tomorrow, user.id)

# Batch operations
class BatchProcessor
  include Sidekiq::Worker
  
  def perform(batch_id)
    Batch.find(batch_id).process!
  rescue => e
    self.class.perform_in(1.hour, batch_id)
    raise e
  end
end

ActiveJob

class ProcessImageJob < ApplicationJob
  queue_as :default
  retry_on Net::OpenTimeout, wait: 5.seconds, attempts: 3
  
  def perform(image)
    image.process!
    image.create_thumbnails!
  end
end

# Usage
ProcessImageJob.perform_later(image)
ProcessImageJob.set(wait: 1.hour).perform_later(image)

Rails Caching

# Fragment caching
<% cache @product do %>
  <%= render @product %>
<% end %>

# Russian doll caching
<% cache ['v1', @product] do %>
  <% cache @product.reviews.maximum(:updated_at) do %>
    <%= render @product.reviews %>
  <% end %>
<% end %>

# Low-level caching
class Product < ApplicationRecord
  def expensive_calculation
    Rails.cache.fetch("product/#{id}/calculation", expires_in: 1.hour) do
      # Expensive operation
      perform_complex_calculation
    end
  end
end

# Cache stores
Rails.cache.read('key')
Rails.cache.write('key', value, expires_in: 1.hour)
Rails.cache.delete('key')
Rails.cache.clear

# Redis caching
redis = Redis.new
redis.set('key', 'value')
redis.get('key')
redis.setex('key', 3600, 'value')  # With expiry
redis.del('key')

16. Security Best Practices

SQL Injection Prevention

# UNSAFE - SQL injection vulnerability
User.where("name = '#{params[:name]}'")

# SAFE - Using placeholders
User.where("name = ?", params[:name])
User.where(name: params[:name])

# SAFE - Named placeholders
User.where("created_at > :date", date: 1.week.ago)

Mass Assignment Protection

# Strong parameters in controllers
def user_params
  params.require(:user).permit(:name, :email)
end

# Never do this
User.create(params[:user])  # Dangerous!

# Always use strong parameters
User.create(user_params)

CSRF Protection

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  
  # For APIs
  protect_from_forgery with: :null_session
end

# In forms
<%= form_with model: @user do |f| %>
  # CSRF token automatically included
<% end %>

XSS Prevention

# Automatically escaped in views
<%= @user.bio %>  # Safe

# Raw HTML (dangerous if user input)
<%= raw @user.bio %>  # Unsafe
<%= @user.bio.html_safe %>  # Unsafe

# Sanitize user input
<%= sanitize @user.bio, tags: %w[p br strong em] %>

Authentication & Authorization

# Secure password handling
class User < ApplicationRecord
  has_secure_password  # Uses bcrypt
  
  validates :password, length: { minimum: 8 }
end

# Session security
class SessionsController < ApplicationController
  def create
    user = User.find_by(email: params[:email])
    if user&.authenticate(params[:password])
      session[:user_id] = user.id
      redirect_to root_path
    else
      flash[:alert] = "Invalid credentials"
      render :new
    end
  end
  
  def destroy
    session.delete(:user_id)
    redirect_to login_path
  end
end

# Authorization with Pundit
class PostPolicy
  def update?
    user.admin? || record.user == user
  end
end

17. Ruby Best Practices

Naming Conventions

class ClassName          # CamelCase
def method_name         # snake_case
CONSTANT_NAME = 1       # SCREAMING_SNAKE_CASE
local_variable = 1      # snake_case
@instance_variable = 1  # snake_case with @

Idiomatic Ruby

# Use symbols for hash keys
{ name: "John" }  # Good
{ "name" => "John" }  # Less idiomatic

# Use unless for negative conditions
puts "Error" unless valid?  # Good
puts "Error" if !valid?     # Less idiomatic

# Use ||= for memoization
@user ||= User.find(id)

# Use &: for simple method calls
users.map(&:name)  # Good
users.map { |u| u.name }  # Verbose

# Use guard clauses
return unless valid?  # Good
if valid?            # Less clean
  # lots of code
end

18. Debugging & Tools

Debugging Techniques

# Print debugging
puts variable.inspect
p variable  # shorthand

# Pry - Interactive debugging
require 'pry'
binding.pry  # breakpoint

# Byebug - Step-through debugging
require 'byebug'
byebug  # breakpoint

# Logger
logger = Logger.new(STDOUT)
logger.info "Information"
logger.error "Error occurred"

Introspection Methods

object.class          # Object's class
object.methods        # Available methods
object.ancestors      # Inheritance chain
object.respond_to?(:method)  # Check method exists
defined?(variable)    # Check if defined
object.object_id      # Unique identifier

19. Quick Reference

Type Checking & Conversions

# Type checking
1.is_a?(Integer)     # true
[].is_a?(Array)      # true
nil.nil?             # true

# Conversions
"5".to_i            # 5
5.to_s              # "5"
:symbol.to_s        # "symbol"

Truth Values

# Only nil and false are falsey
# Everything else is truthy:
0        # truthy
""       # truthy
[]       # truthy

Common Pitfalls

# String interpolation only in double quotes
'Hello #{name}'  # Literal text
"Hello #{name}"  # Interpolated

# Array vs splat
def method(*args)  # args is array
def method(args)   # args is single param

# Symbol vs String keys
{ a: 1 }[:a]   # 1
{ a: 1 }["a"]  # nil

# Return in blocks vs lambdas
Proc.new { return }.call  # Returns from method
lambda { return }.call    # Returns from lambda

Key Takeaways: Ruby prioritizes developer happiness and code readability. Focus on understanding metaprogramming, Rails patterns, and performance optimization for interviews.

Found this useful? Pass it on.
Pro · $10/mo

The sheet is free. Pro goes deeper.

Pro opens the full question library behind every sheet, every refresher and a monthly AI allowance. One subscription, all formats.

Full question library All refreshers Cancel anytime