Getting Started Last updated: Feb. 28, 2026, 4:20 p.m.

The "Getting Started" phase of Rails is centered on the Convention over Configuration (CoC) philosophy. Instead of requiring developers to spend days defining where files should go or how database tables should map to objects, Rails provides a standardized directory structure and a "Golden Path." By running a single command, rails new, the framework generates a complete, bootable ecosystem including a web server, a testing suite, and a database abstraction layer.

This section emphasizes the Full-Stack nature of the framework. Unlike "micro" frameworks that only handle routing, Rails provides a curated collection of tools that work together seamlessly. This allows developers to focus on the unique business logic of their application—the "what"—rather than the repetitive infrastructure—the "how." It is designed to take a developer from a blank terminal to a functional "Hello World" in minutes.

Rails Overview & Philosophy

Ruby on Rails is a model–view–controller (MVC) framework, providing default structures for a database, a web service, and web pages. It encourages and facilitates the use of web standards such as JSON or XML for data transfer and HTML, CSS, and JavaScript for user interfacing. The framework is designed to make programming web applications easier by making assumptions about what every developer needs to get started. It allows you to write less code while accomplishing more than many other languages and frameworks.

The philosophy of Rails is guided by two primary principles:Don't Repeat Yourself (DRY) and Convention over Configuration (CoC). DRY is a principle of software development which states that "Every piece of knowledge must have a single, unambiguous, authoritative representation within a system." By not writing the same information over and over again, the code is more maintainable, more extensible, and less buggy. CoC means that Rails has opinions about the best way to do many things in a web application. Instead of requiring you to configure every minute detail through endless XML or YAML files, Rails follows conventions that allow it to "just work" if you follow the established naming and directory structures.

Installation & Setup

Before installing Rails, you must ensure that your environment has a compatible version of the Ruby programming language, a database engine (such as SQLite, PostgreSQL, or MySQL), and a package manager for JavaScript dependencies (typically Yarn or npm). Rails uses RubyGems to manage its library dependencies. The installation process generally begins with updating the local system dependencies and then installing the Rails gem itself.

Warning: Always ensure you are using a Ruby version manager like rbenv or rvm. Using the system Ruby provided by your OS can lead to permission issues and conflicts during gem updates.

    Dependency Purpose Minimum Version (Recommended)
    Ruby The underlying programming language. 3.2.0+
    SQLite3 Default lightweight database for development. 3.22.0+
    Node.js Required for JavaScript asset compilation. 18.x+
    Yarn Manages frontend packages and dependencies. 1.22+

To install Rails, you execute the following command in your terminal:

# Update RubyGems and install the Rails gem
gem update --system
gem install rails

After the installation completes, verify the installation by checking the Rails version to ensure the binary is correctly mapped in your path.

rails --version
# Output: Rails 7.1.x (or current stable version)

Creating a New Rails Project

Generating a new Rails application is handled by the rails new command-line tool. This command creates a directory structure that contains all the files and folders necessary to run a Rails application. When you run this command, Rails initializes a Git repository, runs bundle install to fetch required gems, and configures the default application settings.

The command accepts several flags that allow you to customize the initial stack, such as choosing a specific database or skipping certain frameworks like Action Mailer or Action Cable if they are not required for your project.

    Argument/Flag Description Example
    app_name The name of the directory and the application module. my_app
    --database Specifies the database engine to be used. -d postgresql
    --javascript Configures the JS bundling approach (importmap, esbuild). -j esbuild
    --css Sets the CSS processor (tailwind, bootstrap, sass). -c tailwind

To create a standard application using the default SQLite database, use:

rails new my_api_project
cd my_api_project

Once inside the directory, you can start the built-in Puma web server to confirm the scaffolding was successful.

bin/rails server 

Hello World (MVC Basic Flow)

The "Hello World" implementation in Rails serves as the primary introduction to the Model-View-Controller (MVC) architectural pattern. In Rails, a request is first intercepted by the Router, which determines which Controller should handle the logic. The Controller then interacts with the Model (if data is needed) and finally renders a View the user.

To implement a basic "Hello World," you must first define a route in config/routes.rb. This tells Rails that a specific URL pattern should map to a specific action inside a controller.

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

Next, the controller must be created. The controller is a Ruby class that inherits from ApplicationController. Inside this class, you define methods (actions) that correspond to the routes.

# app/controllers/pages_controller.rb
class PagesController < ApplicationController
  def hello
    @message = "Hello, Rails!"
  end
end 

Finally, the View provides the HTML structure. Rails uses Embedded Ruby (ERB) files, which allow you to mix standard HTML with Ruby code. By convention, the view file must be located in a folder named after the controller and a file named after the action.

 <%# app/views/pages/hello.html.erb %>
<h1>Greeting Page</h1>
<p><%= @message %></p>

Note

Instance variables defined in the controller action (starting with @) are automatically made available to the corresponding view template. Local variables (without @) are not shared with the view.

Models (Active Record) Last updated: Feb. 28, 2026, 4:20 p.m.

Models in Rails represent the Business Logic and data integrity of the application. Through Active Record, Rails implements an Object-Relational Mapping (ORM) system that allows developers to interact with database rows as if they were standard Ruby objects. This abstraction layer is what makes Rails "database agnostic," allowing you to switch from SQLite in development to PostgreSQL in production with minimal configuration changes.

Beyond simple data retrieval, Models are the primary gatekeepers of Data Integrity. This is where validations (ensuring an email is formatted correctly) and callbacks (triggering an email after a user signs up) reside. By keeping logic in the Model—a practice known as "Skinny Controller, Fat Model"—applications remain easier to test and maintain, ensuring that the rules governing your data are centralized and consistent across the entire app.

Active Record Basics

Active Record is the Model layer of the MVC pattern in Rails. It is an implementation of the Active Record pattern, which is a technique for managing data in relational databases. In Rails, Active Record serves as an Object-Relational Mapping (ORM) system. This means it maps the tables in your database to Ruby classes (Models) and the rows in those tables to objects (Instances) of those classes. By using Active Record, developers can interact with the database using pure Ruby code, abstracting away the complexities of writing raw SQL for common Create, Read, Update, and Delete (CRUD) operations.

The power of Active Record lies in its ability to automatically sense the database schema. When a class inherits from ApplicationRecord, Rails introspects the database table associated with that class and automatically creates accessor methods for every column. For example, if a users table has an email column, the User model will automatically have email and email= methods.

Naming Conventions

Active Record relies heavily on naming conventions to map models to database tables without explicit configuration. By default, Rails pluralizes the class name to find the corresponding table name. If you violate these conventions, Rails provides methods to manually override the table name or primary key, though this is generally discouraged.

    Entity Naming Convention Example
    Model Class Singular, CamelCase UserContact
    Database Table Plural, snake_case user_contacts
    Primary Key Always named id id (Integer/UUID)
    Foreign Key singular_table_name_id user_id
CRUD Operations

Active Record provides a robust API for interacting with data. These operations are handled through class methods for fetching data and instance methods for modifying individual records.

# CREATE
# Create and save a new record to the database
user = User.create(name: "John Doe", email: "john@example.com")

# READ
# Find the first user in the database
user = User.first
# Find a user by a specific attribute
user = User.find_by(email: "john@example.com")
# Find a user by their primary key
user = User.find(1)

# UPDATE
# Change an attribute and save it
user.update(name: "Jane Doe")

# DELETE
# Remove the record from the database
user.destroy 
Database Migrations

In Rails, you do not modify the database schema by writing SQL manually. Instead, you use Migrations. Migrations are a DSL (Domain Specific Language) for describing changes to your database over time in a consistent and repeatable way. They are stored as files in db/migrate and allow team members to stay in sync with the current state of the database.

Note

Active Record Migrations are version-controlled. Each migration file is timestamped, ensuring that Rails executes them in the correct chronological order when you run bin/rails db:migrate.

 # Example migration to create a products table
class CreateProducts < ActiveRecord::Migration[7.1]
  def change
    create_table :products do |t|
      t.string :name
      t.text :description
      t.decimal :price, precision: 8, scale: 2
      t.boolean :is_published, default: false

      t.timestamps # Automatically adds created_at and updated_at columns
    end
  end
end
Schema and Metadata

Active Record automatically manages two special columns via the t.timestamps macro: created_at and updated_at.M These are automatically populated by Rails when a record is first created or subsequently modified. Furthermore, Rails maintains a db/schema.rb file, which is the authoritative source for your current database structure. You should never edit this file directly; it is automatically updated whenever you run migrations.

Warning: When performing bulk operations like update_all or delete_all, Active Record bypasses model instantiations. This means that validations and callbacks (like updating the updated_at timestamp) will not be triggered. Use these methods with caution.

Migrations (Database Changes)

Migrations are a robust feature of Active Record that allow developers to evolve the database schema over time in a consistent and easy way. Rather than managing raw SQL scripts, migrations use a Ruby DSL to describe changes to your tables. This approach ensures that schema changes are platform-independent and can be shared across multiple development environments through version control.

Each migration represents a new "version" of the database. When you run a migration, Rails tracks which versions have already been applied by storing the migration’s unique timestamp in a special metadata table called schema_migrations. This prevents the same change from being applied twice and allows for easy "rolling back" if a change causes issues.

Anatomy of a Migration File

A migration file consists of a class that inherits from ActiveRecord::Migration, tagged with the specific Rails version it was created for (e.g., [7.1]). The primary method within this class is change, which Rails uses to determine how to apply the migration and, in many cases, how to automatically reverse it.

class CreateArticles < ActiveRecord::Migration[7.1]
  def change
    create_table :articles do |t|
      t.string :title
      t.text :body
      t.boolean :published, default: false

      t.timestamps
    end
  end
end 

In more complex scenarios where a migration is not automatically reversible (such as removing a column or changing a data type), you must replace the change method with distinct up and down methods to explicitly define the migration and rollback logic.

Common Migration Methods

Active Record provides a wide array of methods to transform your database. These methods translate directly into the appropriate SQL syntax for your specific database engine (PostgreSQL, MySQL, or SQLite).

    Method Purpose Example
    create_table Creates a new table with a block for column definitions. create_table :users do |t| ... end
    add_column Adds a new column to an existing table. add_column :users, :email, :string
    remove_column Deletes a column from a table. remove_column :posts, :slug, :string
    rename_column Renames an existing column. rename_column :users, :pass, :password
    add_index Adds a database index for performance or uniqueness. add_index :users, :email, unique: true
    add_reference Creates a foreign key column (belongs_to relationship). add_reference :posts, :user, foreign_key: true
Running and Reverting Migrations

The command-line interface for migrations is accessed via bin/rails. These commands allow you to move forward through pending migrations or return to a previous state.

# Apply all pending migrations
bin/rails db:migrate

# Roll back the very last migration performed
bin/rails db:rollback

# Roll back the last 3 migrations
bin/rails db:rollback STEP=3

# Check the status (up/down) of all migrations
bin/rails db:migrate:status 

Warning: Never modify a migration file that has already been shared and run in a production environment. Doing so creates a "schema mismatch" between environments. If you need to fix a mistake in an existing table, generate a new migration to apply the fix.

Supported Data Types

When defining columns within a migration, Active Record maps Ruby-like symbols to the underlying database's native types.

    Type Ruby/Database Representation Use Case
    :string VARCHAR(255) Short text like names or titles.
    :text TEXT Long-form content like blog posts or comments.
    :integer INT Whole numbers, counts, or IDs.
    :decimal DECIMAL Financial data where precision is required.
    :datetime DATETIME / TIMESTAMP Precise points in time.
    :jsonb JSONB (PostgreSQL only) Structured, searchable document data.

Note

The t.timestamps method is a macro that adds two columns: created_at and updated_at. Rails handles the population of these fields automatically whenever a record is created or saved via Active Record.

Validations (Data Integrity)

Validations are used to ensure that only valid data is saved into your database. In the Rails MVC hierarchy, validations occur at the Model level. While database-level constraints are also important, model-level validations are more flexible, easier to test, and provide a better user experience by allowing Rails to gracefully handle errors and display them back to the user before the database layer is even reached.

Validations are triggered whenever an object is saved to the database. By default, Rails runs validations when you call save, create, or update. If any validation fails, the object is marked as invalid, Rails does not perform the SQL INSERT or UPDATE operation, and the save method returns false.

Common Validation Helpers

Active Record offers many pre-defined validation helpers that cover common requirements. These helpers are declared at the top of your model class and can accept various options to customize their behavior.

    Helper Description Example Usage
    presence Ensures the specified attributes are not empty. validates :name, presence: true
    uniqueness Ensures the value is unique across the table. validates :email, uniqueness: true
    length Constrains the character count of an attribute. validates :bio, length: { maximum: 500 }
    numericality Ensures the attribute has only numeric values. validates :age, numericality: { only_integer: true }
    format Validates value against a Regular Expression. validates :code, format: { with: /\A[A-Z]+\z/ }
    inclusion Ensures the value is within a specific set/array. validates :size, inclusion: { in: %w(small medium large) }
Implementation Example

The following code demonstrates how to apply multiple validations to a User model. Notice how we can combine multiple constraints into a single line or separate them for clarity.

class User < ApplicationRecord
  # Ensures name exists and is at least 2 characters
  validates :name, presence: true, length: { minimum: 2 }

  # Ensures email is present, unique, and matches a basic regex
  validates :email, 
            presence: true, 
            uniqueness: { case_sensitive: false }, 
            format: { with: URI::MailTo::EMAIL_REGEXP }

  # Ensures age is a number and at least 18
  validates :age, numericality: { greater_than_or_equal_to: 18 }
end 

Working with Validation Errors

When a validation fails, Rails populates an errors collection on the model instance. You can use this collection to debug why a record is invalid or to display error messages in your frontend views.

 user = User.new(name: "J")
if user.invalid?
  puts user.errors.full_messages
  # Output: ["Name is too short (minimum is 2 characters)", "Email can't be blank"]
end

# Checking specific field errors
user.errors[:email] # => ["can't be blank"]

Conditional Validations and Triggers

Sometimes a validation should only run under specific circumstances. You can use the :if and :unless options, which take a symbol (referring to a method), a string, or a Proc. Additionally, you can specify exactly when a validation should run using the :on option.

    Option Purpose Example
    :on Specifies the action (create or update). validates :email, presence: true, on: :create
    :allow_nil Skips validation if the value is nil. validates :bio, length: { maximum: 100 }, allow_nil: true
    :if Runs validation only if a condition is met. validates :card_number, presence: true, if: :paid_with_card?

Warning: Certain methods in Active Record bypass validations entirely. Methods like save(validate: false),update_column, update_all, and touch will persist data to the database even if it violates your model's validation rules. Use these with extreme caution to avoid data corruption.

Note

Note: For complex validation logic that cannot be handled by built-in helpers, you can define "Custom Validators" by inheriting from ActiveModel::Validator or by writing a private method within your model and calling validate :your_method_name.

Callbacks (Hooks)

Callbacks are methods that get called at specific moments in the lifecycle of an Active Record object. They allow you to trigger logic before or after a change in the object's state, such as when it is created, updated, or destroyed. This is particularly useful for automating tasks like data normalization, complex logging, or updating associated records without cluttering the controller logic.

By hooking into the lifecycle, you ensure that specific business rules are enforced every time a model instance is persisted, regardless of where the call originates in your application.

The Callback Lifecycle

The Active Record lifecycle is highly granular. Callbacks are executed in a specific order surrounding the database operation. If any "before" callback throws an error or explicitly halts the process, the entire transaction is rolled back, and the operation (save, update, or destroy) returns false.

    Category Available Hooks
    Object Creation before_validation, after_validation, before_save, around_save, before_create, around_create, after_create, after_save, after_commit
    Object Update before_validation, after_validation, before_save, around_save, before_update, around_update, after_update, after_save, after_commit
    Object Destruction before_destroy, around_destroy, after_destroy, after_commit
Implementation Example

The most common use for callbacks is data preparation. In the example below, we ensure that an email address is downcased before saving it to the database to prevent duplicate accounts with varying cases, and we trigger a notification after a record is successfully written to the disk.

class User < ApplicationRecord
  # Normalize data before validation
  before_validation :normalize_email

  # Trigger logic only after the database transaction is finalized
  after_commit :send_welcome_email, on: :create

  private

  def normalize_email
    self.email = email.downcase.strip if email.present?
  end

  def send_welcome_email
    UserMailer.welcome_email(self).deliver_later
  end
end 
Halting Execution

In Rails 5 and later, callbacks no longer halt if they return false. To stop the lifecycle (for instance, to prevent a record from being deleted), you must explicitly throw an abort signal. This is a critical safety mechanism when dealing with sensitive data.

 class Order < ApplicationRecord
  before_destroy :ensure_not_shipped

  private

  def ensure_not_shipped
    if shipped?
      errors.add(:base, "Cannot delete a shipped order")
      throw(:abort) # This is the correct way to halt the callback chain
    end
  end
end
Transactional Callbacks

There are two special callbacks that run outside of the standard database transaction: after_commit and after_rollback. These are essential when you need to interact with external systems (like sending an email, clearing a cache, or hitting an API).

Using a standard after_save for these tasks is risky because the after_save runs inside the transaction. If the transaction fails after the code executes, your external service will have been triggered for data that doesn't actually exist in your database.

    Callback Purpose Best Use Case
    after_commit Runs after the DB transaction is finalized. Sending emails or background job queuing.
    after_rollback Runs if the DB transaction fails. Cleaning up temporary files or logging failures.
    after_initialize Runs whenever an object is instantiated. Setting default values for new objects.
    after_find Runs when Rails loads a record from the DB. Decrypting sensitive data for use in Ruby.

Warning: Be cautious of "Callback Hell." Overusing callbacks can make models difficult to test and debug, as logic may trigger unexpectedly. If a callback involves complex logic that isn't strictly related to data integrity, consider moving it to a Service Object.

Note

Just like validations, certain methods bypass callbacks. update_column, update_all, delete, and delete_all will perform database operations without triggering any defined hooks.

Associations (Relations between Models)

In Rails, associations are connections between two Active Record models. They make common operations simpler and easier to write by allowing you to declare the relationship between models in a declarative way. Without associations, you would need to manually manage foreign keys and write complex finders to retrieve related data. By declaring associations, Rails handles the "heavy lifting" of the underlying SQL joins and foreign key assignments.

Associations tell Rails that two models have a connection, and they provide a suite of methods to the model instances to interact with that connection. For example, if a User has many Posts, declaring this association allows you to call user.posts to retrieve an array of post objects rather than writing a manual SQL query.

Types of Associations

Rails supports several types of associations, each representing a different kind of relationship in the database schema. Choosing the correct association depends on where the foreign key is located and the nature of the relationship (singular vs. plural).

    Association Description Foreign Key Location
    belongs_to Sets up a one-to-one connection with another model. The model declaring it.
    has_one Sets up a one-to-one connection where the other model has the key. The associated model.
    has_many A one-to-many relationship. The associated model.
    has_many :through A many-to-many relationship using a join model. The join table.
    has_and_belongs_to_many A many-to-many relationship without a join model. A separate join table.
One-to-Many: The Most Common Relationship

In a one-to-many relationship, one model (the parent) can have many instances of another model (the child), while each child belongs to exactly one parent.

# app/models/author.rb
class Author < ApplicationRecord
  has_many :books,? dependent: :destroy
end

# app/models/book.rb
class Book < ApplicationRecord
  belongs_to :author
end 

With this setup, Rails automatically provides methods to manage the relationship:

  • author.books (returns a collection)
  • author.books << book (adds a book and saves it)
  • book.author (returns the author object)

Many-to-Many:has_many :through

When two models need to share many instances of each other, a join model is recommended. This approach is superior to has_and_belongs_to_many because it allows you to store additional metadata on the relationship itself (e.g., the date a user joined a group).

 # app/models/physician.rb
class Physician < ApplicationRecord
  has_many :appointments
  has_many :patients, through: :appointments
end

# app/models/appointment.rb (The Join Model)
class Appointment < ApplicationRecord
  belongs_to :physician
  belongs_to :patient
end

# app/models/patient.rb
class Patient < ApplicationRecord
  has_many :appointments
  has_many :physicians, through: :appointments
end
Polymorphic Associations

Polymorphic associations allow a model to belong to more than one other model on a single association. For example, you might have a Comment model that can belong to both aPost and a Video.

 # app/models/comment.rb
class Comment < ApplicationRecord
  belongs_to :commentable, polymorphic: true
end

# app/models/post.rb
class Post < ApplicationRecord
  has_many :comments, as: :commentable
end

# app/models/video.rb
class Video < ApplicationRecord
  has_many :comments, as: :commentable
end

Warning: When using has_many, be mindful of the :dependent option. If you delete a parent record, associated child records might become "orphaned" in the database. Use dependent: :destroy to ensure associated records are also removed, or dependent: :nullify to set their foreign keys to NULL.

Important Options

Associations can be customized using various options to change their behavior.

    Option Purpose
    class_name Specifies the actual class name if it differs from the association name.
    foreign_key Manually specifies the foreign key column name.
    dependent Defines what happens to associated records when the owner is destroyed.
    validate If true, validates associated objects whenever the owner is saved.

Note

Active Record associations use Lazy Loading. If you call Author.all.each { |a| puts a.books.count }, Rails will execute one query for the authors and one additional query for each author's books. To avoid this "N+1 query" performance bottleneck, always use includes (e.g., Author.includes(:books).all).

Query Interface (Finding Data)

The Active Record Query Interface is a powerful DSL that allows you to retrieve data from your database using Ruby methods instead of writing raw SQL. It is designed to be highly readable and chainable, meaning you can combine multiple methods to build complex queries dynamically. Under the hood, Active Record generates highly optimized SQL specific to your database engine (PostgreSQL, MySQL, or SQLite).

One of the most important aspects of the query interface isLazy Loading (or Method Chaining). When you call a method like where or order, Active Record does not immediately execute the query. Instead, it returns an ActiveRecord::Relation object. The SQL query is only executed when the data is actually needed (e.g., when you iterate over the results or call a method like .to_a).

Retrieving Single Objects

When you need to find a specific record, Rails provides several methods that differ in how they handle missing data and which columns they use for the search.

    Method Description Behavior if Not Found
    find(id) Finds a record by its primary key. Raises ActiveRecord::RecordNotFound
    find_by(...) Finds the first record matching the criteria. Returns nil
    first Returns the first record (ordered by primary key). Returns nil
    last Returns the last record (ordered by primary key). Returns nil
    find_or_create_by Finds a record or creates it if it doesn't exist. Returns the found or new object

												
# Finding by ID
user = User.find(1)

# Finding by specific attributes
user = User.find_by(email: 'test@example.com', active: true)

# Using find_by! to raise an error if not found
user = User.find_by!(username: 'gemini_user')
Filtering and Ordering

To retrieve collections of records, you use methods that define the WHERE, ORDER, and LIMIT clauses of a SQL statement.

 # Filtering with conditions
published_posts = Post.where(status: 'published')

# Using placeholders to prevent SQL Injection
# ALWAYS use this syntax for user-provided input
secure_search = Post.where("title LIKE ?", "%#{params[:query]}%")

# Ordering results
recent_posts = Post.order(created_at: :desc)

# Limiting and Offsetting
paged_posts = Post.limit(10).offset(20)

# Selecting specific columns to save memory
post_titles = Post.select(:id, :title)
Calculations and Aggregations

Active Record provides built-in methods for performing common mathematical operations directly in the database, which is significantly faster than loading all records into Ruby memory to calculate them.

    Method Purpose Example
    count Counts the number of records. User.count
    average Calculates the average of a column. Product.average(:price)
    minimum / maximum Finds the lowest or highest value. Order.maximum(:total)
    sum Adds up all values in a column. Donation.sum(:amount)
Eager Loading (Solving the N+1 Problem)

A common performance pitfall in Rails is the "N+1 query" problem. This occurs when you load a collection of records and then perform a separate query for each record to fetch an association. To prevent this, use includes or joins.

 # BAD: Triggers 1 query for authors + N queries for their books
authors = Author.all
authors.each { |a| puts a.books.name }

# GOOD: Triggers only 2 queries total
authors = Author.includes(:books).all
authors.each { |a| puts a.books.name }

Warning: Never use string interpolation in query methods (e.g., where("name = '#{name}'")). This exposes your application to SQL Injection attacks. Always use the array or hash syntax provided by Rails to safely sanitize inputs.

Scopes

Scopes allow you to define commonly-used queries inside your model, making your controller code cleaner and more expressive.

 class Article < ApplicationRecord
  scope :published, -> { where(published: true) }
  scope :created_since, ->(time) { where("created_at > ?", time) }
end

# Usage:
@recent_articles = Article.published.created_since(1.day.ago)

Note

ActiveRecord::Relation objects are chainable. You can define a scope and then chain further where, order, or limit calls onto it seamlessly.

Views (Action View) Last updated: Feb. 28, 2026, 4:21 p.m.

Views are responsible for the Presentation Layer, turning raw data from the models into the final HTML, JSON, or XML sent to the user's browser. Rails uses Embedded Ruby (ERB) by default, which allows developers to sprinkle Ruby code directly into HTML templates. The goal of the View layer is to remain as "dumb" as possible, focusing purely on display logic while delegating complex calculations back to the models or controllers.

To keep views clean and reusable, Rails provides View Helpers and Partials. Partials allow you to extract common UI elements (like a navigation bar or a comment form) into small, manageable snippets that can be rendered across different pages. This promotes the DRY (Don't Repeat Yourself) principle, ensuring that a change to a single partial updates the UI across the entire application simultaneously.

Layouts and Rendering

In the Rails MVC architecture, Action View is responsible for compiling the response sent back to the browser. Rendering is the process of transforming your data and templates into a final format, usually HTML. Rails distinguishes between the Template (the specific content for an action) and the Layout (the global wrapper containing elements like headers, footers, and navigation). This separation ensures that you maintain a consistent look and feel across your application without duplicating code.

When a controller action finishes executing, Rails automatically looks for a template file that matches the name of the controller and the action. This is part of the "Convention over Configuration" philosophy. However, you can explicitly control this behavior using the render method to specify different templates, partials, or even raw text and JSON.

The Layouts Folder

Layouts reside in the app/views/layouts directory. By default, Rails uses application.html.erb as the global wrapper for all controllers. Within a layout, the yield statement is the most critical component; it identifies where the content from the specific action template should be injected.

 <%# app/views/layouts/application.html.erb %>
<!DOCTYPE html>
<html>
  <head>
    <title>MyDocsApp</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>
    <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
  </head>
  <body>
    <nav>
      <a href="/">Home</a>
    </nav>

    <main>
      <%# This is where the specific view content is injected %>
      <%= yield %>
    </main>

    <footer>
      <p>&copy; 2026 Documentation Project</p>
    </footer>
  </body>
</html>

The render Method

The render method is versatile. While Rails defaults to rendering the template corresponding to the current action, you can override this logic to handle different business flows, such as re-displaying a form when validations fail.

Handling Multiple Yields
Command Description Example
render :edit Renders the edit.html.erb template from the current controller. render :edit
render "products/show" Renders a template from a different controller. render "admin/dashboard"
render layout: false Renders the template without the global application layout. render :popup, layout: false
render json: ... Converts an object to JSON and sets the appropriate HTTP headers. render json: @user
render plain: ... Sends a plain text response to the browser. render plain: "OK"
Handling Multiple Yields

Sometimes a layout needs more than one insertion point—for example, a sidebar that changes based on the page or a specific set of scripts for the <head>. Rails provides content_for to capture a block of code in the view and inject it into a named yield in the layout.

 
<%# In the Layout: app/views/layouts/application.html.erb %>
<aside>
  <%= yield :sidebar %>
</aside>

<%# In the View: app/views/articles/show.html.erb %>
<% content_for :sidebar do %>
  <ul>
    <li>Author: <%= @article.author %></li>
    <li>Published: <%= @article.created_at.to_date %></li>
  </ul>
<% end %>

<h1><%= @article.title %></h1>
<p><%= @article.content %></p>
Rendering Partial Templates

Partials are a way to break your view code into smaller, reusable chunks. Partial filenames always begin with an underscore (e.g., _form.html.erb), but they are referenced without the underscore when called.

 <%# Rendering a partial for a form %>
<%= render "form", article: @article %>

<%# Rendering a collection (Automatically iterates and passes each element) %>
<%= render partial: "comment", collection: @article.comments %>

Note

When rendering a collection using render @comments, Rails will look for a partial named _comment.html.erb and automatically pass a local variable named comment to each iteration. This is a highly optimized shortcut for clean views.

Warning: You can only call renderM or redirect_to once per controller action. If your logic allows for multiple paths, ensure you use return to exit the method after the first render call, otherwise, Rails will raise a DoubleRenderError.

Component Responsibility
Template Contains the specific HTML and logic for a single page/action.
Layout Provides the boilerplate HTML structure and global elements.
Partial Sub-templates used for reusability (e.g., a "User Card" or "Navigation Link").
Yield A placeholder in the layout where the template or content_for blocks are inserted.

Form Helpers

In Ruby on Rails, form helpers are designed to simplify the creation of HTML forms while ensuring tight integration with Active Record models. Rather than writing raw HTML <form> and <input> tags, developers use Ruby methods that automatically handle authenticity tokens (for CSRF protection), generate appropriatename and id attributes based on model attributes, and pre-fill fields with existing data.

Rails categorizes form helpers into two main types: Generic Form Helpers (used for forms not tied to a specific model) and Model-Specific Form Helpers (used to create or update database records).

The form_withHelper

Starting with Rails 5.1 and standardized in later versions, form_with is the unified method for creating forms. It can handle both model-backed forms and basic URL-based forms. By default, form_with submits via Ajax (XHR), though this can be disabled by setting local: true.

Option Description Example
:model Ties the form to a specific Active Record object. form_with model: @user
:url Specifies the submission path manually. form_with url: search_path
:method Defines the HTTP verb (GET, POST, PATCH, DELETE). method: :patch
:scope Adds a namespace to the input names. scope: :admin
Model-Backed Forms

When you pass an object to form_with, Rails uses Polymorphic Routing to determine the URL and method. If the @article object is new (not yet saved in the DB), the form will point to the create action using POST. If the object already exists, it will point to the update action using PATCH.

 <%# app/views/articles/_form.html.erb %>
<%= form_with model: @article do |form| %>
  <div>
    <%= form.label :title, "Article Title" %>
    <%= form.text_field :title, placeholder: "Enter title here..." %>
  </div>

  <div>
    <%= form.label :content %>
    <%= form.text_area :content, size: "60x10" %>
  </div>

  <div>
    <%= form.label :status %>
    <%= form.select :status, ['draft', 'published'], selected: 'draft' %>
  </div>

  <div>
    <%= form.submit "Save Article" %>
  </div>
<% end %>
Common Input Helpers

Rails provides a wide variety of helpers that map to specific HTML5 input types, ensuring better mobile keyboard support and data validation.

Helper HTML Equivalent Purpose
text_field <input type="text"> Standard single-line text input.
password_field <input type="password"> Masks characters as they are typed.
email_field <input type="email"> Triggers email keyboards on mobile devices.
number_field <input type="number"> Numeric input with min, max, and step support.
date_select Multiple <select> tags Provides dropdowns for Year, Month, and Day.
check_box <input type="checkbox"> Boolean values (includes a hidden field for "false").
Handling Selection and Associations

Creating dropdowns or radio buttons for associated data is a common task. The collection_select helper is specifically designed to generate a <select> tag based on a collection of Active Record objects.

<%# Creating a dropdown of all Categories in the database %>
<%= form.collection_select :category_id, Category.all, :id, :name, prompt: true %> 

Note

The check_box helper generates a hidden field with a value of 0 before the actual checkbox. This ensures that if the user unchecks the box, a value is still sent to the controller (otherwise, the browser would send nothing, and the attribute would remain true in the database).

Form Security (CSRF)

Rails protects against Cross-Site Request Forgery (CSRF) by automatically embedding an authenticity_token in every form. When the form is submitted, Rails verifies this token against the one stored in the user's session. If they do not match, the request is rejected.

Warning: If you write a form using raw HTML <form> tags instead of Rails helpers, you must manually include the CSRF token using the csrf_meta_tags or hidden_field_tag, otherwise, your POST requests will fail with an InvalidAuthenticityToken error.

Asset Pipeline (CSS & JavaScript)

The Asset Pipeline is a framework within Rails (historically powered by Sprockets, now evolved through Propshaft andWebpacker) designed to process and serve frontend assets like JavaScript, Stylesheets, and Images. Its primary purpose is to provide a "production-ready" frontend by concatenating files to reduce HTTP requests, minifying code to reduce file size, and transpiling high-level languages (like Sass or TypeScript) into browser-compatible CSS and JavaScript.

Modern Rails (Version 7+) has shifted toward import maps as the default, which allows you to load JavaScript modules directly in the browser without complex bundling. However, for more intensive frontend requirements, Rails still supports traditional bundlers like esbuild, Rollup, or Webpack.

The Manifest and Fingerprinting

In a production environment, Rails uses Fingerprinting. This process appends a unique MD5 hash based on the file content to the filename (e.g., application-d3b073.css). This ensures that if the content changes, the filename changes, forcing the user's browser to bypass its cache and download the new version.

Asset Type Source Directory Helper Method
Stylesheets app/assets/stylesheets stylesheet_link_tag
JavaScript app/javascript javascript_importmap_tags or javascript_include_tag
Images app/assets/images image_tag
Fonts/Other app/assets/fonts asset_path
Implementation: Stylesheets and Images

To include assets in your layouts, you use specific Rails helper methods. These methods are preferred over standard HTML tags because they automatically handle the pathing for both development and production (fingerprinted) environments.

 <%# app/views/layouts/application.html.erb %>

<%# Link to the main stylesheet %>
<%= stylesheet_link_tag "application", media: "all", "data-turbo-track": "reload" %>

<%# Render an image from app/assets/images/logo.png %>
<%= image_tag "logo.png", alt: "Company Logo", class: "header-logo" %>
Modern JavaScript: Import Maps

With Rails 7+, the default isImport Maps. This technology avoids the "Node.js dependency hell" by mapping logical names to specific file paths or CDNs, allowing the browser to manage modules natively.

 # config/importmap.rb

# Pinning a local file
pin "application", preload: true
# Pinning a remote library via a CDN (JSPM)
pin "canvas-confetti", to: "https://ga.jspm.io/npm:canvas-confetti@1.6.0/dist/confetti.browser.js"

In your JavaScript file, you can then import these modules directly:

 // app/javascript/application.js
import confetti from "canvas-confetti"

document.addEventListener("turbo:load", () => {
  console.log("Assets loaded via Import Maps!");
  confetti();
});
Precompilation for Production

In development, Rails compiles assets on-the-fly. However, in production, assets must be pre-generated into the public/assets folder. This is done via a Rake task. This step is critical because the web server (like Nginx or Apache) will serve these static files directly for maximum performance.

 # Command to prepare assets for a production deployment
bin/rails assets:precompile
Organizing CSS with Sass

Rails includes built-in support for Sass (Syntactically Awesome Style Sheets). This allows for variables, nesting, and mixins, making your CSS more maintainable. By using the .erb extension on an asset file (e.g., custom.css.scss.erb), you can even embed Ruby code to reference paths or configuration variables.

 /* app/assets/stylesheets/variables.scss */
$primary-color: #3b82f6;

.btn-primary {
  background-color: $primary-color;
  &:hover {
    background-color: darken($primary-color, 10%);
  }
}

Warning: Never reference assets using hardcoded strings like/assets/logo.png. Always use the image_tag or asset_path helpers. If you use hardcoded strings, your images will break in production because the fingerprinted filenames (e.g., logo-abc123.png) will not match your code.

Note

As of Rails 7, Turbo (part of Hotwire) is the default. It handles page navigation without full reloads. When writing JavaScript, use the turbo:load event instead of DOMContentLoaded to ensure your scripts execute correctly when navigating between pages.

Import Maps (Modern JS Handling)

Import maps are the default mechanism for managing JavaScript in Rails 7+. Historically, Rails relied on heavy-duty bundlers like Webpack to transpile and bundle JavaScript into a single file. Import maps represent a paradigm shift by leveraging native browser support for ES Modules (ESM). Instead of "building" a JavaScript bundle, Rails provides the browser with a map that translates logical module names (e.g., "bootstrap") into specific versioned file paths.

This approach significantly simplifies the development workflow by removing the requirement for Node.js or a node_modules directory in many standard Rails applications. It allows for faster page loads through better caching, as changing one small JavaScript file no longer invalidates a massive monolithic bundle.

The importmap.rb Configuration

The heart of this system is the config/importmap.rb file. This file acts as a manifest where you "pin" your dependencies. When you pin a library, you are telling Rails where to find the source code, whether it is a local file within your application or a remote package hosted on a CDN (Content Delivery Network) like JSPM or UNPKG.

# config/importmap.rb

# Pin the main entry point of the application
pin "application", preload: true

# Pin a local component
pin "dropdown_controller", to: "controllers/dropdown_controller.js"

# Pin a remote library from a CDN
pin "canvas-confetti", to: "https://ga.jspm.io/npm:canvas-confetti@1.6.0/dist/confetti.browser.js" 
Usage in JavaScript Files

Once a library is pinned, you can use the standard JavaScript import syntax. The browser intercepts these requests, looks at the map provided in the HTML header, and fetches the correct file.

 // app/javascript/application.js
import { confetti } from "canvas-confetti"

document.addEventListener("turbo:load", () => {
  console.log("Import maps are handling this module!");
  confetti();
});
Managing Dependencies via CLI

Rails provides a dedicated command-line tool to manage your import map pins. This tool automatically fetches the correct URLs from CDNs and updates yourconfig/importmap.rb file, ensuring that sub-dependencies (transitively required libraries) are also pinned.

Command Action
bin/importmap pin jquery Finds and pins the jQuery library to the config file.
bin/importmap unpin jquery Removes the library from the config file.
bin/importmap json Outputs the raw JSON map that will be sent to the browser.
bin/importmap audit Checks for known security vulnerabilities in pinned packages.
Preloading for Performance

One potential downside of ES Modules is "waterfalling," where the browser discovers it needs a dependency only after downloading the parent file. Rails mitigates this using the preload: true option in the pin. This adds a <link rel="modulepreload"> tag to the HTML head, instructing the browser to begin downloading critical scripts immediately.

 <%# app/views/layouts/application.html.erb %>
<head>
  <%# This helper generates the actual <script type="importmap"> block and preloads %>
  <%= javascript_importmap_tags %>
</head>

Note

Import maps work best with libraries that are distributed as ES Modules. If you need to use older "legacy" JavaScript libraries that rely on global variables or CommonJS (require), you may still need to use a bundler like esbuild.

Warning: Since Import Maps rely on native browser features, they are supported only in modern browsers. While this covers >95% of users, if your application must support very old browsers (like Internet Explorer), you will need to use a transpiler and bundler instead of Import Maps.

Feature Import Maps Traditional Bundlers (Webpacker/esbuild)
Node.js Required No Yes
Build Step None (Native ESM) Required (Transpilation/Minification)
Caching Granular (Per-file) Coarse (The whole bundle)
Complexity Low High

Controllers (Action Controller) Last updated: Feb. 28, 2026, 4:21 p.m.

Controllers act as the Middleman or "Traffic Cop" of the Rails MVC architecture. When a user enters a URL, the Router directs the request to a specific Controller action. The controller’s job is to gather the necessary data from the Models and pass it off to the Views for rendering. It manages the "flow" of the application, handling things like user sessions, redirects, and responding to different file formats.

A key responsibility of the Controller is Security and Filtering. It uses Before Actions to ensure a user is logged in before viewing a page and Strong Parameters to whitelist exactly which pieces of data are allowed to be saved to the database. By acting as a protective layer between the public internet and your private data, the Controller ensures that only authorized actions are performed.

Controller Overview

In the Rails MVC architecture, the Controller acts as the central coordinator or the "brain" of the request-response cycle. Its primary responsibility is to receive an incoming HTTP request from the router, interact with the Model to retrieve or persist data, and then instruct the View to render the appropriate output. In a well-architected Rails application, controllers should remain "skinny," meaning they contain only the logic necessary to direct data between the Model and the View, while complex business logic is encapsulated within the Model or Service Objects.

Every controller in a Rails application is a Ruby class that inherits from ApplicationController, which in turn inherits from ActionController::Base. This inheritance chain provides the controller with a vast array of methods for handling sessions, cookies, redirects, and rendering.

Naming and Directory Structure

Following the Convention over Configuration principle, controllers are named using the plural form of the resource they manage, followed by the suffix Controller. The file names must be snake_case, while the class names are CamelCase.

Resource Controller Class Name File Path
Article ArticlesController app/controllers/articles_controller.rb
User UsersController app/controllers/users_controller.rb
Admin Setting Admin::SettingsController app/controllers/admin/settings_controller.rb
The Action Lifecycle

A controller is composed of public methods called Actions. When a request matches a route, Rails instantiates the controller and calls the specific action method. Once the method completes, Rails automatically looks for a view template that matches the action name unless an explicit render orredirect_to is specified.

 class ArticlesController < ApplicationController
  # GET /articles
  def index
    # Interacting with the Model
    @articles = Article.all
    
    # Implicitly renders app/views/articles/index.html.erb
  end

  # GET /articles/:id
  def show
    @article = Article.find(params[:id])
  end
end

Theparams Hash

All data sent with a request—whether it comes from a URL segment (like /articles/1), a query string (like ?page=2), or a submitted form—is accessible through the params hash. This object behaves like a Ruby hash but allows you to access keys using either strings or symbols interchangeably.

Param Type Source Example Access Method
Path Parameter /photos/:id params[:id]
Query String /search?q=ruby params[:q]
Form Data <input name="user[name]"> params[:user][:name]
Strong Parameters

As a security measure to prevent Mass Assignment vulnerabilities (where a malicious user could pass extra fields like admin=true into a form), Rails requires developers to explicitly whitelist which parameters are allowed for database operations. This is handled via the require and permit methods.

 class UsersController < ApplicationController
  def create
    @user = User.new(user_params)
    if @user.save
      redirect_to @user
    else
      render :new
    end
  end

  private

  # Best Practice: Encapsulate logic in a private method
  def user_params
    params.require(:user).permit(:username, :email, :password)
  end
end

Warning: Failure to use Strong Parameters will result in an ActiveModel::ForbiddenAttributesError when trying to pass the entire params hash directly into a model's .new or .create methods.

Note

The ApplicationController as a "base" for your specific controllers. You can define methods or before_action filters here that should apply to every controller in your application, such as user authentication or setting the user's locale.

Routing (The routes.rb file)

The Rails Router is the engine that connects incoming HTTP requests to the appropriate controller actions. When your Rails application receives a request, it consults the config/routes.rb file to find a match. This file acts as a centralized map, defining how URLs should be interpreted. Routing in Rails is designed to be RESTful, meaning it maps HTTP verbs (GET, POST, PATCH, DELETE) and URLs to standard CRUD (Create, Read, Update, Delete) operations.

In addition to mapping requests, the router also generates "URL helpers." These are Ruby methods that allow you to reference paths dynamically in your views and controllers, ensuring that if you change a URL structure in the routes file, you don't have to manually update every link in your application.

Resourceful Routing

The most common way to define routes is via theresources keyword. A single line of code can generate seven different routes for a model, following the REST convention. This keeps the routes file clean and ensures the application follows predictable web standards.

 # config/routes.rb
Rails.application.routes.draw do
  # Generates 7 standard routes for the Articles resource
  resources :articles
end

The resources :articles declaration creates the following mapping:

HTTP Verb Path Controller#Action Named Helper
GET /articles articles#index articles_path
GET /articles/new articles#new new_article_path
POST /articles articles#create articles_path
GET /articles/:id articles#show article_path(:id)
GET /articles/:id/edit articles#edit edit_article_path(:id)
PATCH/PUT /articles/:id articles#update article_path(:id)
DELETE /articles/:id articles#destroy article_path(:id)
Customizing Resources

Sometimes you don't need all seven default routes, or you need to add specific functionality that doesn't fit the standard CRUD pattern. Rails allows you to restrict routes using only or except, and add custom actions using member or collection blocks.

resources :photos, only: [:index, :show] do
  # Member routes act on a specific record (requires an ID)
  member do
    get 'preview' # /photos/:id/preview
  end

  # Collection routes act on the whole set
  collection do
    get 'search'  # /photos/search
  end
end 
The Root Route

The root route is a special designation that determines what a user sees when they visit the base URL of your application (e.g., www.example.com). It should always be defined at the top or bottom of the draw block.

 # Maps the home page to the index action of the HomeController
root "home#index"
Non-Resourceful (Direct) Routes

For pages that don't represent a database resource (like an "About" or "Contact" page), you can define manual GET routes.

get "/about", to: "pages#about", as: :about_us 
  • to: specifies the controller#action.
  • as: creates the named helper methods (about_us_path and about_us_url).
Nested Resources

When one resource logically belongs to another (e.g., Comments belonging to an Article), you can nest them to reflect this relationship in the URL structure.

resources :articles do
  resources :comments
end
# Resulting path: /articles/:article_id/comments/:id 

Warning: Avoid nesting resources more than one level deep. Deeply nested routes (e.g., /users/1/articles/5/comments/10) result in long, confusing URLs and make controller logic significantly more complex. Use "Shallow Nesting" if you need to reference a child's ID directly.

Note

You can view all defined routes in your application at any time by running bin/rails routes in your terminal or by visiting /rails/info/routes your browser while in development mode.

Parameters & Strong Parameters

In Rails, Parameters are the data sent by the user to the application via the browser. This data can arrive through the URL path, a query string, or a submitted form. All these inputs are collected into a single, convenient object called params. Because this data comes from an external source (the user), it is inherently untrusted. Strong Parameters is the security layer that prevents "Mass Assignment" vulnerabilities by forcing developers to explicitly whitelist which attributes are allowed to be passed to the database.

The params Hash

The params object is an instance of ActionController::Parameters. While it acts like a Ruby hash, it provides security methods and allows you to access keys using either strings or symbols.

Parameter Type Example URL/Source Access Syntax
Path Params /users/:id (e.g., /users/5) params[:id] # => "5"
Query Params /search?q=ruby&page=1 params[:q] # => "ruby"
Body Params Form inputs like name="user[email]" params[:user][:email]
Mass Assignment Security

Mass Assignment is a feature where you pass a hash of attributes to a model to create or update a record in one go (e.g.,User.new(params[:user])). Without Strong Parameters, a malicious user could add an unauthorized field to the request, such asadmin: true, and potentially grant themselves administrative privileges.

Strong Parameters solve this by requiring two specific actions:

  1. Require: Ensures that a specific key (usually the model name) is present in the params hash.
  2. Permit: Specifies which sub-attributes are allowed to be processed.
Standard Implementation

The best practice is to wrap parameter logic in a private method at the bottom of your controller. This keeps your actions clean and allows the logic to be reused across create and update actions.

 class ProductsController < ApplicationController
  def create
    # Only the permitted attributes will be passed to .new
    @product = Product.new(product_params)
    
    if @product.save
      redirect_to @product
    else
      render :new
    end
  end

  private

  def product_params
    # 1. Look for the :product key
    # 2. Only allow :name, :description, and :price
    params.require(:product).permit(:name, :description, :price)
  end
end
Handling Complex Data

Sometimes parameters are not just simple strings. You may need to handle arrays (like tags) or nested data (like an address belonging to a user).

Scenario Code Example
Scalar Values params.permit(:title, :content)
Arrays params.permit(tags: [])
Nested Hashes params.require(:user).permit(:name, address: [:street, :city])
Handling "Unpermitted Parameters"

By default, Rails will simply ignore any keys that are not explicitly permitted. However, in a development environment, Rails will log a message in your terminal: Unpermitted parameter: :hacker_field.

  • To raise an error in development: You can configure Rails to raise an exception if unpermitted keys are found by setting config.action_controller.action_on_unpermitted_parameters = :raise in your config/environments/development.rb.

Warning: Never use params.permit!. This method permits every key in the hash, effectively disabling the security benefits of Strong Parameters and leaving your database vulnerable to mass assignment attacks.

Note

If you are building an API that receives JSON, Rails automatically parses the JSON body into the params hash, allowing you to use the same require and permit logic as you would with a standard HTML form.

Session & Cookies

In a standard web environment, HTTP is stateless, meaning the server treats every request as a completely new interaction with no memory of previous ones. To build features like user logins or shopping carts, Rails uses Sessions and Cookies to maintain state between requests.

A CookieM is a small piece of data stored on the user's browser. A Session is a higher-level abstraction that allows you to store data about a user that persists as they move from page to page.

Sessions vs. Cookies

While they work together, they serve different roles in data management and security.

Feature Cookies Sessions
Storage Location Client's Browser Server-side (managed by Rails)
Data Capacity Limited (~4KB) Virtually unlimited (depending on store)
Security Visible/Editable by user (unless signed/encrypted) Highly secure; user only sees a "Session ID"
Common Use Remember me, Tracking, Preferences Login status, Shopping carts, User IDs
Working with the Session

The session method in Rails works like a Ruby hash. You can store values in it during one action and retrieve them in another. Rails automatically encrypts the session cookie to prevent users from tampering with the data.

 class SessionsController < ApplicationController
  def create
    user = User.find_by(email: params[:email])
    if user&.authenticate(params[:password])
      # Storing the user_id in the session
      session[:user_id] = user.id
      redirect_to root_path, notice: "Logged in!"
    end
  end

  def destroy
    # Removing data from the session
    session[:user_id] = nil
    redirect_to root_path, notice: "Logged out!"
  end
end
Working with Cookies

You can also interact with cookies directly. This is useful for data that doesn't need to be strictly tied to a "session" (like a "dark mode" preference) or for "Remember Me" functionality.

  • Plain Cookies: Visible to the user in the browser console.
  • Signed Cookies: Cryptographically signed so the user cannot change them.
  • Encrypted Cookies: Signed and encrypted so the user cannot even read the content.
 # Setting a permanent, encrypted cookie
cookies.encrypted.permanent[:login_token] = "secret_token_123"

# Reading a cookie
token = cookies.encrypted[:login_token]

# Deleting a cookie
cookies.delete(:login_token)
Session Storage Options

By default, Rails uses the CookieStore. This means the entire session hash is encrypted and stored in a cookie on the user's browser. For larger applications, you might change this in config/initializers/session_store.rb.

Store Type Pros Cons
CookieStore Default, zero-config, fast. 4KB limit; can't easily "invalidate" all sessions.
CacheStore Extremely fast (uses Memcached/Redis). Data is volatile; if the cache clears, users are logged out.
ActiveRecordStore Permanent; allows managing active sessions. Slower (requires database hits).

The flashHash

The flash is a special part of the session that only persists until the next request. It is designed specifically for passing success or error messages to the user.

 def update
  if @article.update(article_params)
    flash[:success] = "Article updated!"
    redirect_to @article
  else
    # use flash.now when rendering (current request)
    flash.now[:error] = "Update failed."
    render :edit
  end
end

Warning: Never store large objects (like a full User model) directly in the session. This will exceed the 4KB limit and degrade performance. Instead, store only the ID of the object (e.g., session[:user_id] = user.id) and look up the record in the controller.

Hash

Request Forgery Protection

Cross-Site Request Forgery (CSRF) is an attack where a malicious site tricks a user’s browser into performing an unwanted action on a different site where the user is currently authenticated. Because browsers automatically include cookies (and thus session data) with every request to a specific domain, a blind request from a malicious site could delete data or change passwords without the user's knowledge.

Rails provides built-in protection against this by ensuring that every "non-GET" request (POST, PUT, PATCH, DELETE) originates from your own application.

How It Works: The Authenticity Token

When CSRF protection is enabled, Rails generates a unique, non-guessableauthenticity token for each user session.

  1. The Server: Embeds this token in every form (as a hidden field) and in the HTML head (as a meta tag).
  2. The Client: When a form is submitted, the token is sent back to the server.
  3. The Verification: Rails compares the token in the request with the token stored in the user's session. If they do not match, Rails resets the session or raises an error.
Implementation in Controllers

By default, the ApplicationController includes a call to protect_from_forgery. This applies the protection to every controller in your app that inherits from it.

class ApplicationController < ActionController::Base
  # Enables CSRF protection
  protect_from_forgery with: :exception
end
 
Strategy Behavior on Mismatch Use Case
:exception Raises ActionController::InvalidAuthenticityToken. Standard web applications (strict security).
:null_session Clears the session for the duration of the request. APIs that support cookie-based auth but allow external calls.
:reset_session Completely destroys the user's session. High-security applications to prevent further hijacking.
Ensuring Tokens are Included

If you use Rails helpers, the token is managed for you automatically.

  • Forms: form_with or form_tag automatically inserts a hidden <input type="hidden" name="authenticity_token">.
  • AJAX/JavaScript: If you are using Fetch or XHR, you must pull the token from the meta tags and include it in the X-CSRF-Token header.
 <%# app/views/layouts/application.html.erb %>
<html>
  <head>
    <%# Generates: <meta name="csrf-token" content="..."> %>
    <%= csrf_meta_tags %>
  </head>
  ...
</html>
Skipping Protection

In specific cases, such as an API that uses Token-based authentication (like JWT) instead of Cookies, you may need to disable CSRF protection because the request does not rely on browser cookies to authenticate.

 class Api::V1::BaseController < ActionController::Base
  # Skip CSRF protection for API calls
  skip_before_action :verify_authenticity_token
end
Best Practices
Rule Reason
Use GET only for reading Browsers and Rails assume GET requests are "safe" and do not check for tokens. Never change data in a GET action.
Use Rails Form Helpers Prevents forgetting the hidden token field.
Keep csrf_meta_tags Essential for JavaScript-based requests (like Turbo or Axios) to function.

Warning: If you receive an InvalidAuthenticityToken error in development, it usually means your session has expired, you've hardcoded an HTML form without a token, or you are trying to submit a form via JavaScript without including the CSRF header.

Advanced Core Components Last updated: Feb. 28, 2026, 4:21 p.m.

As applications grow in complexity, the standard MVC pattern is supplemented by Specialized Services designed to handle modern web requirements. These components manage "out-of-band" tasks that would otherwise slow down the main request-response cycle. For example, sending an email or processing a heavy image is offloaded to background workers, while real-time updates are pushed to users via persistent socket connections.

These advanced tools—ranging from file attachment management to rich text editing—are integrated directly into the Rails core. This means they share the same configuration patterns and "magic" as the rest of the framework. By providing high-level abstractions for complex technologies like WebSockets and Cloud Storage, Rails allows small teams to build features that would typically require an entire DevOps department in other ecosystems.

Active Job (Background Tasks)

Active Job is a framework for declaring background jobs and making them run on a variety of queuing backends. These jobs can be anything from regularly scheduled cleanups to billing charges or sending emails. The primary goal is to ensure that "heavy" tasks are handled outside of the main request-response cycle, keeping your application snappy and responsive for the user.

Without background jobs, a user would have to wait for a slow process (like generating a PDF or hitting a third-party API) to finish before the page reloads. With Active Job, you "enqueue" the task and immediately tell the user, "We're working on it!"

Why Use Background Jobs?

The main request-response loop should ideally take less than 200ms. If a task takes longer, it should be offloaded.

Use Case Description
Email Delivery Sending newsletters or password resets without pausing the UI.
Image Processing Resizing or watermarking uploaded photos.
Third-Party APIs Syncing data with Salesforce, Stripe, or Slack.
Heavy Calculations Generating complex financial reports or data exports.
Creating and Enqueuing a Job

You can generate a new job using the Rails generator: bin/rails generate job ProcessReport. This creates a class in app/jobs.

 # app/jobs/process_report_job.rb
class ProcessReportJob < ApplicationJob
  queue_as :default

  # The logic to be executed in the background
  def perform(user_id)
    user = User.find(user_id)
    # Simulate heavy work
    ReportGenerator.generate_for(user)
  end
end

To trigger this job from a controller or model, you use perform_later:

 # Enqueue for as soon as the queue is free
ProcessReportJob.perform_later(current_user.id)

# Enqueue to run at a specific time
ProcessReportJob.set(wait: 1.hour).perform_later(current_user.id)
Supported Queuing Backends

Active Job is an adapter layer. In development, it uses the :async adapter (in-memory), but for production, you need a persistent store.

Backend Storage Best For
Sidekiq Redis High-performance, multi-threaded execution (Most Popular).
Solid Queue Database (SQL) Simplicity; no need for a separate Redis server (Rails 8 default).
Delayed Job Database (SQL) Ease of setup in environments where Redis isn't available.
Amazon SQS AWS Cloud Large-scale enterprise applications on AWS.
GlobalID: Passing Objects to Jobs

Active Job uses GlobalID to handle arguments. Instead of passing a large Ruby object (which would fail to serialize into the queue), you pass the object itself, and Rails automatically converts it to a URI.

 # Rails handles this automatically
# Enqueue: "gid://app/User/1"
ProcessReportJob.perform_later(@user)

# Perform: Rails finds the user by ID before running the method
def perform(user)
  puts user.email
end
Callbacks and Retries

Jobs can fail (e.g., an API is down). Active Job provides built-in mechanisms to handle these failures gracefully.

class ProcessReportJob < ApplicationJob
  # Automatically retry the job if the remote server is down
  retry_on Net::OpenTimeout, wait: :exponentially_longer, attempts: 5

  # Log a specific message if the record was deleted before the job ran
  discard_on ActiveRecord::RecordNotFound

  before_perform do |job|
    # Logic to run before the job starts
  end
end 

Warning: Never pass sensitive data or large files directly as arguments to a job. Pass the database ID or a file path. The queue store (like Redis) is often unencrypted and has size limits.

Note

Remember that background jobs run in a separate process. They do not have access to the session, cookies, or current_user from your controller. You must pass any necessary data as arguments.

Action Mailer (Sending Emails)

Action Mailer allows you to send emails from your application using a philosophy similar to controllers and views. Mailers inherit from ActionMailer::Base and reside in app/mailers. Just as a controller action renders a view to produce an HTML response for a browser, a mailer action renders a template to produce the body of an email.

Mailer Components

A mailer setup consists of three primary parts:

  1. The Mailer Class: Defines the "action" and sets the to, from, and subject headers.
  2. The Mailer View: An .erb template (HTML or Plain Text) that defines the email content.
  3. The Configuration: Settings in config/environments that define the delivery method (SMTP, SendGrid, Postmark, etc.).
Creating a Mailer

Use the generator to create a new mailer: bin/rails generate mailer UserMailer.

 # app/mailers/user_mailer.rb
class UserMailer < ApplicationMailer
  default from: 'notifications@example.com'

  def welcome_email(user)
    @user = user
    @url  = 'http://example.com/login'
    
    # mail() triggers the rendering of the template
    mail(to: @user.email, subject: 'Welcome to My Awesome Site')
  end
end
Mailer Views

Emails should generally provide both HTML and Plain Text versions to ensure compatibility across all mail clients. Rails looks for both files automatically if they share the same name.

  • HTML: app/views/user_mailer/welcome_email.html.erb
  • Text: app/views/user_mailer/welcome_email.text.erb
<%# app/views/user_mailer/welcome_email.html.erb %>
<h1>Welcome to Example.com, <%= @user.name %></h1>
<p>
  You have successfully signed up. Your username is: <%= @user.username %>.<br>
</p>
<p>
  To login to the site, just follow this link: <%= link_to 'Login', @url %>.
</p> 
Sending Emails

Mailers integrate seamlessly with Active Job. You should almost always use deliver_later to avoid making the user wait for the SMTP server to respond during a web request.

Method Execution Type Description
deliver_now Synchronous Sends the email immediately. Blocks the current process.
deliver_later Asynchronous Enqueues the email as a background job (Recommended).
 # From a controller action
UserMailer.welcome_email(@user).deliver_later
Previewing Emails

Rails provides a built-in way to preview emails in the browser without actually sending them. This is located in test/mailers/previews. You can view these at localhost:3000/rails/mailers.

 # test/mailers/previews/user_mailer_preview.rb
class UserMailerPreview < ActionMailer::Preview
  def welcome_email
    UserMailer.welcome_email(User.first)
  end
end
Delivery Methods & Configuration

In your environment files (e.g., config/environments/production.rb), you must specify how Rails should hand off the email.

Method Best For Description
:test Testing Emails are added to an array (ActionMailer::Base.deliveries) rather than sent.
:letter_opener Development Opens the email in a new browser tab instead of sending it.
:smtp Production Connects to a standard mail server (Gmail, Outlook, etc.).
:sendmail Unix Systems Uses the system's local sendmail installation.

Warning: Emails sent from Rails use Absolute URLs. In your mailer views, you must use _url helpers (e.g., user_url(@user)) instead of_path helpers, and you must configure config.action_mailer.default_url_options so Rails knows your domain name.

Action Text (Rich Text Content)

Action Text makes it easy to handle and display rich text content (formatted text, images, and attachments) in Rails. It integrates the Trix editor into the framework, handling everything from the frontend user interface to the backend storage of attachments via Active Storage.

Instead of storing raw HTML in a standard string or text column, Action Text stores the content in a separate ActionText::RichText model that is associated with your existing models. This keeps your main database tables lean and provides a standardized way to handle complex content.

Key Features
  • WYSIWYG Editing: Provides the Trix editor out of the box for bolding, lists, and links.
  • Integrated Attachments: Users can drag and drop images or files directly into the editor.
  • Seamless Transformation: Automatically handles the conversion of embedded attachments into optimized HTML tags.
Setup and Installation

To use Action Text, you must first run the installer to create the necessary database tables and migration files.

 # Install Action Text and Active Storage dependencies
bin/rails action_text:install
bin/rails db:migrate
Implementation in Models and Views

You don't need to add a column to your model's table. Instead, you declare the rich text attribute in the model file using has_rich_text.

1. The Model
# app/models/article.rb
class Article < ApplicationRecord
  # This creates a virtual attribute 'content'
  has_rich_text :content
end 

2. The Form View Use the rich_text_area helper to render the Trix editor in your forms.

 <%# app/views/articles/_form.html.erb %>
<%= form_with model: @article do |form| %>
  <%= form.label :content %>
  <%= form.rich_text_area :content %>
  <%= form.submit %>
<% end %>

3. Displaying Content display the formatted content, simply render the attribute. Rails will handle the sanitization and attachment rendering.

<%# app/views/articles/show.html.erb %>
<h1><%= @article.title %></h1>
<div>
  <%= @article.content %>
</div> 
Comparison: Plain Text vs. Action Text
Feature Standard text Column Action Text
Formatting Plain text only (unless manual HTML) Rich text (Bold, Italics, Lists)
Images Requires manual URL handling Drag-and-drop via Active Storage
Database Stored in the model's table Stored in action_text_rich_texts
Sanitization Manual or none Automatic via Rails
Handling Attachments

When a user uploads an image through Action Text, it is stored using Active Storage. You can customize how these attachments are rendered by modifying the partial located at app/views/active_storage/blobs/_blob.html.erb.

Note

Because Action Text content is stored in a separate table, you should use eager loading to avoid N+1 queries when displaying a list of records: Article.all.with_rich_text_content

Warning: Action Text content is sanitized to prevent XSS attacks. If you need to allow specific HTML tags that are stripped by default, you must configure the ActionText::ContentHelper.allowed_tags in an initializer.

Active Storage (File Uploads)

Active Storage is a built-in Rails framework that facilitates uploading files to a cloud storage service (like Amazon S3, Google Cloud Storage, or Microsoft Azure Storage) or a local disk for development. It attaches those files to Active Record objects without requiring you to add specific columns to your model's table for file paths.

Instead of managing file metadata manually, Active Storage uses two primary database tables: active_storage_blobs (metadata like filename and content type) and active_storage_attachments (the "join" table connecting the blob to your model).

Key Features
  • Polymorphic Links: Attach files to any model using a simple declaration.
  • Image Processing: Built-in support for generating variants (resizing, cropping) using Vips or ImageMagick.
  • Direct Uploads: Allows the browser to upload files directly to the cloud, bypassing your Rails server to save bandwidth and memory.
  • Previewing: Generates image previews for non-image files like PDFs and videos.
Setup and Configuration

First, run the installer to generate the necessary migrations for the metadata tables:

 bin/rails active_storage:install
bin/rails db:migrate

Next, configure your storage services in config/storage.yml and point to the desired service in your environment files (e.g., config/environments/development.rb ).

# config/storage.yml
local:
  service: Disk
  root: <%= Rails.root.join("storage") %>

amazon:
  service: S3
  access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
  # ... other AWS config 
Attaching Files to Models

You can define either a single attachment or multiple attachments on a model.

Macro Usage Example
has_one_attached For a single file (e.g., Avatar). has_one_attached :avatar
has_many_attached For multiple files (e.g., Gallery). has_many_attached :photos
# app/models/user.rb
class User < ApplicationRecord
  has_one_attached :avatar do |attachable|
    # Define variants directly in the model (Rails 7+)
    attachable.variant :thumb, resize_to_limit: [100, 100]
  end
end 
Working with Attachments in Views

Rails provides helpers to generate file inputs and display the attached files.

1. The Form
 <%= form_with model: @user do |form| %>
  <%= form.file_field :avatar %>
  <%= form.submit %>
<% end %>
2. Displaying the Image
<% if @user.avatar.attached? %>
  <%# Display the processed thumbnail variant %>
  <%= image_tag @user.avatar.variant(:thumb) %>
<% end %> 
Comparison: Attachment Types
Feature has_one_attached has_many_attached
Logic Replaces any existing file on upload. Appends new files to the collection.
Access Returns a single object. Returns an ActiveStorage::Attached::Many collection.
Use Case Profile pictures, resumes, single logos. Image galleries, document attachments.
Performance: Eager Loading

Because Active Storage uses separate tables, displaying a list of records with their attachments can cause N+1 queries. Use the provided scopes to fetch everything in one go:

 # Fetches users and their avatars in a single query
@users = User.with_attached_avatar.all

Warning: To use image resizing/variants, you must have the libvips or ImageMagick library installed on your system and the image_processing gem in your Gemfile.

Note

For security, Active Storage serves files via a redirection controller. When you call url_for(user.avatar), Rails generates a short-lived URL that expires, preventing unauthorized hotlinking of your private files.

Action Cable (WebSockets/Real-time)

Action Cable integrates WebSockets into the Rails framework, allowing for real-time, bi-directional communication between the server and the client. Unlike traditional HTTP requests where the client must poll the server for updates, Action Cable maintains a persistent connection. This enables features like live chat, instant notifications, and real-time dashboard updates.

It combines a JavaScript framework on the client side with a Ruby framework on the server side, providing a "full-stack" approach to real-time features.

Core Concepts

Action Cable uses a "Pub/Sub" (Publisher/Subscriber) model to manage data flow.

Concept Responsibility
Connection A single WebSocket connection per browser tab/window. Handled by ApplicationCable::Connection.
Channel Similar to a Controller, it encapsulates the logic for a specific stream (e.g., ChatChannel).
Pub/Sub The mechanism (usually backed by Redis) that broadcasts messages to all subscribers.
Broadcasting The act of sending data from the server to one or more channels.
Implementation: Server Side

To create a real-time feature, you first define a Channel. You can generate one using bin/rails generate channel Chat.

# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
  # Called when the consumer has successfully subscribed to the channel
  def subscribed
    stream_from "chat_room"
  end

  # Called when data is sent from the client-side channel
  def speak(data)
    # Broadcast the received message to everyone subscribed to "chat_room"
    ActionCable.server.broadcast("chat_room", message: data['message'])
  end
end 
Implementation: Client Side

On the frontend, your JavaScript "subscribes" to the channel and defines how to handle incoming data.

 // app/javascript/channels/chat_channel.js
import consumer from "channels/consumer"

consumer.subscriptions.create("ChatChannel", {
  connected() {
    console.log("Connected to the chat room!");
  },

  received(data) {
    // This code runs whenever the server broadcasts to "chat_room"
    const chatBox = document.getElementById('chat-box');
    chatBox.innerHTML += `<p>${data.message}</p>`;
  },

  send_message(text) {
    // Calls the 'speak' method on the server-side ChatChannel
    this.perform('speak', { message: text });
  }
});
Broadcasting from Anywhere

One of the most powerful features of Action Cable is the ability to trigger a broadcast from other parts of your application, such as a Controller or a Background Job.

# From a controller after a record is saved
def create
  @message = Message.create(message_params)
  if @message.save
    ActionCable.server.broadcast("chat_room", message: @message.content)
  end
end 
Deployment & Dependencies

Action Cable requires a "Subscription Adapter" to manage the delivery of messages.

  • Development: Uses the async adapter (in-memory, works within a single process).
  • Production: Requires Redis. Since production Rails apps usually run multiple processes (Puma workers), Redis acts as the central hub that coordinates broadcasts across all processes.

Note

In Rails 7+, Action Cable is often used in tandem with Hotwire (Turbo Streams). Turbo Streams uses Action Cable behind the scenes to update parts of a page automatically without writing custom JavaScript.

Warning: Be mindful of authentication. Because WebSockets are long-lived, you should authenticate the connection once in app/channels/application_cable/connection.rb (usually by checking the session a signed cookie) to prevent unauthorized users from subscribing to private data streams.

Security & Maintenance Last updated: Feb. 28, 2026, 4:22 p.m.

Security in Rails is built on the principle of Secure by Default. The framework includes automatic mitigations for the most common web attacks, such as cross-site scripting (XSS) and SQL injection. Rather than requiring developers to remember to sanitize every input, Rails assumes data is unsafe and handles the protection layers automatically unless explicitly told otherwise.

Maintenance is facilitated through a suite of Standardized Tooling. From the command-line interface that allows for easy database migrations to the built-in credential management system, Rails provides a clear path for keeping an application healthy over many years. This includes "Secret Management" and environment-specific configurations that ensure sensitive production keys never leak into the public version control system.

Securing Rails Applications

Rails is famous for its "Secure by Default" philosophy. It includes built-in mitigations for the most common web vulnerabilities identified by the OWASP Top 10. However, security is a process, not a checkbox. While the framework provides the tools, developers must implement them correctly to protect user data and maintain application integrity.

Built-in Protections

Rails provides automatic defense against several major attack vectors without requiring manual configuration.

Attack Type Rails Defense Mechanism Description
Cross-Site Scripting (XSS) Automatic Escaping All strings rendered in ERB templates are escaped by default. <script> becomes &lt;script&gt;.
Cross-Site Request Forgery (CSRF) Authenticity Tokens Requires a unique token for all non-GET requests to ensure they originate from your app.
SQL Injection Parameterized Queries Active Record automatically escapes input in find(id) or where(name: params[:name]).
Clickjacking X-Frame-Options Default headers prevent your site from being embedded in an <iframe> on malicious domains.
Password Security (Has_secure_password)

Never store passwords in plain text. Rails provides the has_secure_password macro, which uses the BCrypt hashing algorithm to salt and hash passwords before they hit the database.

 # app/models/user.rb
class User < ApplicationRecord
  # Requires a 'password_digest' column in the database
  has_secure_password
  
  validates :password, length: { minimum: 8 }
end
Secure Header Management

Rails uses the ActionDispatch::SSL middleware and the content_security_policy initializer to manage browser-level security headers.

  • Force SSL: In config/environments/production.rb, setting config.force_ssl = true ensures all traffic is encrypted and cookies are marked as Secure.
  • Content Security Policy (CSP): Defines which dynamic resources (JS, CSS, Images) are allowed to load, significantly reducing the impact of XSS attacks.
 # config/initializers/content_security_policy.rb
Rails.application.config.content_security_policy do |policy|
  policy.default_src :self, :https
  policy.font_src    :self, :https, :data
  policy.img_src     :self, :https, :data
  policy.object_src  :none
  policy.script_src  :self, :https
end
The Rails Security Audit Tools

Maintaining a secure application requires regular scanning of dependencies for known vulnerabilities.

Tool Purpose Command
Bundler-Audit Scans Gemfile.lock for vulnerable gem versions. bundle exec bundle-audit check --update
Brakeman A static analysis security scanner for Rails code. bundle exec brakeman
Rails Audit Built-in task for basic security checks (Rails 7+). bin/rails app:update (checks config)
Best Practices for Developers
  • Avoid html_safe and raw: Only use these when you are 100% certain the content is not user-generated.
  • Use Strong Parameters: Never pass params directly to a model; always whitelist attributes.
  • Secret Key Base: Ensure your master.key or credentials.yml.enc is never committed to version control. This key is used to sign/encrypt cookies and sessions.
  • Rate Limiting: Use gems like rack-attack to prevent brute-force login attempts and DDoS attacks.

Warning: While Active Record protects against SQL injection in standard hash-based queries, it does not protect you if you pass a raw string with interpolation to a where clause.

\
  • Bad: User.where("name = '#{params[:name]}'")
  • Good: User.where("name = ?", params[:name])

Debugging Rails Applications

Debugging is an essential skill in Rails development, moving from simple log inspection to interactive code execution. Rails provides a suite of tools to help you track down bugs in the logic, the database, or the view layer.

1. The Rails Logger

The logger is your first line of defense. Every request, database query, and background job is logged to log/development.log. You can manually inject messages into this log to track variable states.

Log Level Usage Example
debug Detailed info for troubleshooting. logger.debug "User: #{@user.inspect}"
info General confirmation of app flow. logger.info "Payment processed"
warn Unexpected but non-fatal events. logger.warn "Retry attempt 2"
error Fatal errors that stop a process. logger.error "DB Connection failed"
2. Interactive Debugging with debug

Since Rails 7, the debug gem is the default tool for pausing code execution. By placing the binding.break (or the older debugger) command in your code, Rails will freeze the execution and open an interactive terminal where the code stopped.

 def update
  @article = Article.find(params[:id])
  binding.break # The app pauses here
  if @article.update(article_params)
    redirect_to @article
  else
    render :edit
  end
end
Common Debugger Commands:
  • c (next): Move to the next line.
  • s (step): Step into a method call.
  • c (continue): Resume normal execution.
  • info: View local variables.
3. The Rails Console

The console (bin/rails c) is a "Read-Eval-Print Loop" (REPL) that loads your entire Rails environment. It is the best place to test Model logic or verify data without refreshing the browser.

  • Sandbox Mode: Run bin/rails c --sandbox to test commands that modify the database. All changes will be rolled back when you exit the console.
  • Reloading: If you change your code while the console is open, run reload! to pull in the latest changes.
4. Web-Based Debugging

Rails includes several built-in views to assist with debugging in the development environment.

Tool Access Description
Web Console Bottom of error pages An interactive terminal inside the browser at the point of an exception.
Error Page Browser on crash Shows the stack trace, extracted source code, and request parameters.
Rails Routes /rails/info/routes A searchable list of all URL patterns and their controllers.
5. Debugging in Views

Sometimes you need to see what an object looks like directly on the webpage.

  • debug(object): Renders the object in a YAML format inside a <pre> tag.
  • console: Typing <% console %> inside an ERB template will trigger the Web Console in your browser when that page renders.

Warning: Never leave binding.break, console, or detailed logger.debug statements in your code when pushing to production. They can cause your application to hang or leak sensitive data into the logs.

Note

For performance-related debugging, use the SQL logging in your terminal. If you see dozens of identical queries (e.g., SELECT "comments".* FROM "comments" WHERE "comments"."article_id" = ?), you have an N+1 query problem that should be solved with .includes.

Configuring Rails Applications

Rails follows the "Convention over Configuration" philosophy, but every application eventually requires custom settings for different environments (Development, Test, and Production). Configuration in Rails is centralized, primarily residing in the config/ directory.

The Configuration Hierarchy

Rails loads configuration in a specific order. Settings defined in environment-specific files override the general settings defined in application.rb.

File Scope Purpose
config/application.rb Global Settings that apply to all environments (e.g., time zone, autoload paths).
config/environments/*.rb Environment-Specific Settings for development, test, or production (e.g., caching, log levels).
config/initializers/*.rb Component-Specific Code executed after the framework and gems are loaded (e.g., API keys, devise config).
Environment-Specific Settings

Most of your configuration changes will happen in the environment files to ensure the app behaves appropriately for its context.

# config/environments/production.rb
Rails.application.configure do
  # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
  config.force_ssl = true

  # Log to STDOUT (useful for Docker/Heroku)
  config.log_formatter = ::Logger::Formatter.new
  
  # Set the log level
  config.log_level = :info
end 
Managing Secrets (Credentials)

Rails uses a built-in Encrypted Credentials system to store sensitive information like API keys and database passwords. This allows you to check your credentials into version control securely, as they are encrypted with a master.key.

  • Edit Credentials: Run bin/rails credentials:edit. This opens your system's default editor.
  • Access Credentials:
  •  # Accessing a nested key in your code
    stripe_key = Rails.application.credentials.dig(:stripe, :api_key)
Initializers

Initializers are Ruby files located in config/initializers/. They run once when the application boots. Use these for settings that don't fit into the standard environment files or for configuring third-party gems.

 # config/initializers/time_formats.rb
# Define a custom date format used app-wide
Date::DATE_FORMATS[:short_date] = "%d %b %Y"
Common Configuration Tasks
Task Configuration Location Example
Time Zone application.rb config.time_zone = 'Eastern Time (US & Canada)'
Locale (I18n) application.rb config.i18n.default_locale = :es
Database database.yml Setting host, username, and pool size.
Asset Precompilation production.rb config.assets.compile = false
Custom Configuration

You can also add your own custom configuration settings to the Rails object, making them accessible throughout your application.

 # config/application.rb
config.x.payment_gateway.merchant_id = "MID_12345"

# Accessing it in a controller
merchant_id = Rails.configuration.x.payment_gateway.merchant_id

Warning: Never commit your config/master.key to a git repository. If this key is lost or stolen, anyone can decrypt your production secrets, and you will lose access to your own encrypted data.

\

Note

After changing any file in config/initializers/ or config/environments/, you must restart your Rails server for the changes to take effect.

The Command Line Tools

The Rails Command Line Interface (CLI) is the engine room of development. It follows the principle of "Generators and Tasks," allowing you to automate repetitive coding chores and manage your application's lifecycle without leaving the terminal.

Core Commands

Most Rails commands follow the pattern bin/rails <command> [options]. Using bin/rails ensures you are using the specific version of Rails bundled with your current project.

Command Shortcut Purpose
bin/rails server s Launches the local web server (usually at localhost:3000).
bin/rails console c Opens an interactive Ruby session with your app environment loaded.
bin/rails generate g Creates new code stubs (models, controllers, migrations).
bin/rails db:... N/A Performs database operations (migrate, seed, setup).
bin/rails routes N/A Lists all URL patterns and their corresponding controller actions.
bin/rails test t Runs your test suite.
The Power of Generators

Generators are blueprints that create files based on Rails conventions. They don't just create files; they also update your routes and create test stubs.

  • Model Generator: bin/rails g model Article title:string body:text
    • Creates a migration file and the model file.
  • Controller Generator: bin/rails g controller Products index show
    • Creates the controller, views for index and show, and adds routes.
  • Scaffold Generator: bin/rails g scaffold Task name:string status:integer
    • The "All-in-One": Generates the model, migration, controller, views, tests, and CSS for a full CRUD interface. Use this sparingly in real projects, as it generates a lot of "boilerplate" code.
Database Tasks

The db: namespace is critical for managing your schema and data state.

Task Action
bin/rails db:create Creates the database defined in database.yml.
bin/rails db:migrate Runs pending migrations to update your schema.
bin/rails db:rollback Reverts the last migration performed.
bin/rails db:seed Loads the data defined in db/seeds.rb into the database.
bin/rails db:prepare A "catch-all" that runs create and migrate (perfect for first-time setup).
Specialized Tools

As your app grows, you will use specific tools for assets and background processing.

  • bin/dev:L If you use a CSS/JS bundler (like Tailwind or esbuild), this command starts the Rails server and the asset watchers simultaneously.
  • bin/bundle: Manages your Gemfile dependencies.
  • bin/rails notes: Searches through your code for comments like TODO, FIXME, or OPTIMIZE.
Best Practices
  • Dry Run: Add -d or --pretend to a generate command to see what files will be created without actually creating them.
  • Destroy: If you made a mistake in a generator, use bin/rails destroy <type> <name> (e.g., bin/rails d model Article) to undo all files created by that specific generator.
  • Shortcuts: While bin/rails is standard, many developers use rails directly. However, using bin/rails is safer for ensuring version consistency across different projects on the same machine.

Note

You can see a full list of available tasks by running bin/rails --help or bin/rails -T.

Testing Last updated: Feb. 28, 2026, 4:22 p.m.

Testing is considered a First-Class Citizen in Rails. Every time a developer generates a new model or controller, Rails automatically creates a corresponding test file. This encourages a culture of "Test-Driven Development" (TDD) where the software's behavior is verified by automated scripts. The framework provides a dedicated test database and a built-in runner to ensure the "Safety Net" is always present.

The Rails testing philosophy covers the entire Testing Pyramid. It provides tools for fast, isolated unit tests that check a single piece of logic, as well as "System Tests" that control a real web browser to verify the user experience. By making testing easy to set up and fast to execute, Rails helps developers move quickly with the confidence that new changes haven't introduced "regressions" in older parts of the code.

Testing Rails Applications (Introduction)

Testing is not an "add-on" in the Rails ecosystem; it is a core philosophy. Rails was built with testing in mind from day one, providing a robust infrastructure to ensure that your application behaves as expected as it grows. The goal of testing is to provide a "safety net" so you can refactor code, upgrade gems, or add features without breaking existing functionality.

Why Test in Rails?
  • Prevent Regressions: Ensure that fixing one bug doesn't introduce another.
  • Documentation: Tests serve as a living technical specification of what the code is supposed to do.
  • Confidence: Allows for "Continuous Deployment" where code can be shipped to production automatically if the tests pass.
The Rails Testing Environment

Rails automatically creates a test directory when you generate a new project. It also maintains a separate Test Database so your testing process doesn't interfere with your development data.

Component Purpose
test/models Logic for data validation and business rules.
test/controllers Testing request handling, redirects, and responses.
test/integration Testing the interaction between multiple controllers/models.
test/system Full browser automation (testing the UI like a real user).
test/fixtures Sample data used to populate the test database.
Types of Tests in Rails
Test Level Target Speed Focus
Unit Tests Models Fast Validations, scopes, and logic.
Functional Tests Controllers Medium Status codes, parameters, and redirects.
Integration Tests Full Workflow Slow A user logging in and posting an article.
System Tests Browser/UI Very Slow JavaScript execution and CSS visibility.
Anatomy of a Test

Rails uses Minitest by default (though RSpec is a popular community alternative). A standard test follows the AAA pattern: Arrange (setup data), Act (run the code), and Assert (check the result).

 # test/models/article_test.rb
require "test_helper"

class ArticleTest < ActiveSupport::TestCase
  test "should not save article without title" do
    # 1. Arrange
    article = Article.new
    
    # 2. Act & 3. Assert
    assert_not article.save, "Saved the article without a title"
  end
end
Running Tests

You can run your tests through the command line. Rails provides detailed feedback on which tests passed, failed, or errored.

  • bin/rails test: Runs all tests in the test folder.
  • bin/rails test test/models/article_test.rb: Runs tests for a specific file.
  • bin/rails test:system: Runs only the slow-running browser/system tests.
Fixtures: The Test Data

Instead of manually creating records for every test, Rails uses Fixtures. These are YAML files located in test/fixtures/ that define the initial state of your test database.

# test/fixtures/articles.yml
first_article:
  title: "My First Post"
  body: "This is some content."

second_article:
  title: "A Secret Post"
  body: "Confidential info." 

Note

When you run a test, Rails automatically loads these fixtures into the test database and wraps each test in a Database Transaction. This means any changes made during the test are "rolled back" afterward, ensuring a clean state for the next test.

Warning: Avoid "Testing Implementation Details." Focus your tests on the outcome (e.g., does the user see a success message?) rather than the specific internal method name used to get there.

System Testing

System Tests (also known as End-to-End or Acceptance tests) occupy the top of the testing pyramid. While Model and Controller tests check individual "gears" of the machine, System Tests check theentire machine by automating a real browser. They simulate a user’s journey—clicking buttons, filling out forms, and verifying that JavaScript and CSS are functioning correctly.

In Rails, System Tests are powered by Capybara, which provides a human-readable DSL (Domain Specific Language) to interact with the browser.

Why System Testing is Essential
Feature Benefit
JavaScript Execution Verifies features like modals, dropdowns, and Hotwire/Turbo updates that standard controller tests cannot see.
Visual Verification Ensures elements are actually visible and clickable on the screen.
User Flow Validates that the "Happy Path" (e.g., Sign Up -> Purchase -> Receipt) works from start to finish.
Cross-Browser Can be configured to run against Chrome, Firefox, or Safari to catch browser-specific bugs.

Configuration: test_helper.rb

System tests are configured in test/application_system_test_case.rb. You can choose between "headless" browsers (fast, no UI) or "headed" browsers (opens a window so you can watch the test run).

class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
  # :headless_chrome is faster for CI/CD pipelines
  # :chrome is better for debugging so you can see the actions
  driven_by :selenium, using: :headless_chrome, screen_size: [1400, 1400]
end 
Writing a System Test

You can generate a system test using: bin/rails generate system_test Users.

 # test/system/articles_test.rb
require "application_system_test_case"

class ArticlesTest < ApplicationSystemTestCase
  test "creating a new article" do
    visit articles_path
    
    click_on "New Article"
    
    fill_in "Title", with: "Learning Rails"
    fill_in "Body", with: "System tests are powerful!"
    
    click_on "Create Article"
    
    # Assertions check the actual rendered content in the browser
    assert_text "Article was successfully created"
    assert_selector "h1", text: "Learning Rails"
  end
end
Common Capybara Commands

Capybara allows you to interact with the page as a user would, using simple English-like methods.

Action Type Commands
Navigation visit root_path, go_back
Interaction click_on "Save", fill_in "Email", with: "...", check "Remember Me"
Discovery find(".profile-link"), first(:link, "Edit")
Assertions assert_selector, assert_no_text, assert_button_enabled
Debugging take_screenshot, save_and_open_page
Comparison: System Tests vs. Integration Tests
Feature System Tests Integration Tests
Engine Full Browser (via Selenium/Cuprite) Rack-Test (Fast HTTP simulation)
JavaScript Fully Supported Not Supported
Speed Slow (High overhead) Fast (Low overhead)
Use Case Complex UI and User Journeys API endpoints and simple form posts
Screenshots and Debugging

System tests are notoriously difficult to debug when they fail because the browser state is ephemeral. Rails assists by:

  1. Automatic Screenshots: If a system test fails, Rails automatically saves a screenshot of the browser at the exact moment of failure in tmp/screenshots.
  2. Pause for Inspection: You can insert binding.break in your test code to pause the browser window, allowing you to manually inspect the HTML or CSS.

Warning: Because system tests are slow, avoid writing them for every minor edge case. Use them for your primary user flows and rely on faster Model/Unit tests for logic-heavy edge cases.

Note

For modern Rails apps using Hotwire, system tests are your best friend. Since Hotwire relies on partial DOM updates via WebSockets or AJAX, system tests are often the only way to verify those updates occur correctly.

Integration Testing

Integration tests sit between fast Unit tests and slow System tests. While a Model test checks a single class and a System test checks the entire browser experience, an Integration Test focuses on how different parts of your application (controllers, models, and routes) interact during a request-response cycle.

In Rails, integration tests use the Rack::Test library, which simulates HTTP requests extremely quickly without the overhead of a real browser or JavaScript engine.

The Purpose of Integration Tests

Integration tests are ideal for verifying that your "business workflows" are intact. They ensure that a request hits the right route, the controller manipulates the model correctly, and the user is redirected to the proper destination.

Feature Integration Testing Benefit
Performance Much faster than System tests because they don't load a browser.
Workflow Logic Can follow multiple redirects and maintain session state across requests.
Security Excellent for testing access control (e.g., ensuring a guest cannot access an admin page).
API Testing The standard way to test JSON/REST API endpoints.
Creating an Integration Test

Generate an integration test using: bin/rails generate integration_test UserFlows.

 # test/integration/user_flows_test.rb
require "test_helper"

class UserFlowsTest < ActionDispatch::IntegrationTest
  test "can see the homepage and create an article" do
    # 1. Navigate to the index
    get "/"
    assert_response :success

    # 2. Post data to the create action
    post "/articles", params: { article: { title: "New Blog", body: "Content here" } }
    
    # 3. Follow the redirect to the show page
    follow_redirect!
    assert_response :success
    assert_select "h1", "New Blog"
  end
end
Key Integration Testing Methods

Because integration tests simulate a web client, they provide methods to mimic browser behavior and inspect the resulting server state.

Method Description
get, post, patch, delete Simulates an HTTP request to a specific path.
assert_response Checks the HTTP status (e.g., :success, :redirect, :forbidden).
follow_redirect! Tells the test runner to follow the HTTP 302 redirect to the next page.
assert_select Uses CSS selectors to verify specific HTML elements are present in the response.
host! Allows you to simulate requests coming from a specific domain or subdomain.
Integration vs. Controller Tests

In modern Rails (since version 5), Controller TestsM have largely been superseded by Integration Tests. While you can still write specific controller tests, the Rails team recommends using integration tests for most "request-level" testing.

Aspect Integration Test (Recommended) Controller Test (Legacy Style)
Scope Full stack (Routes -> Controller -> Model) Controller instance only.
Execution Real HTTP simulation via Rack. Direct method calls on controller actions.
Redirection Can follow redirects automatically. Cannot follow redirects; only checks headers.
Testing APIs

Integration tests are the primary tool for testing Rails APIs. Since APIs don't have a UI for System Tests to interact with, you use integration tests to verify JSON structures and status codes.

test "api returns list of products" do
  get "/api/v1/products", as: :json
  assert_response :success
  
  json_response = JSON.parse(response.body)
  assert_equal 3, json_response.length
  assert_equal "Laptop", json_response.first["name"]
end 

Warning:Integration tests cannot test JavaScript. If your feature depends on a React component, a Stimulus controller, or a jQuery script to function, you must use a System Test instead.

Note

Use assert_select sparingly. If you change your CSS classes frequently, your integration tests will break even if the logic is correct. Focus on asserting the presence of data rather than specific styling.

Extending Rails Last updated: Feb. 28, 2026, 4:22 p.m.

One of Rails' greatest strengths is its Extensibility. Through the "Rails Engine" and "Plugin" systems, the framework can be customized or expanded to fit specific needs. An Engine is essentially a mini-application that can be shared across multiple projects, providing its own routes, models, and views. This allows for a modular architecture where massive applications are broken down into smaller, manageable pieces.

Furthermore, Active Support allows developers to extend the Ruby language itself. By adding "Core Extensions" to standard Ruby classes like Strings and Dates, Rails makes the code much more expressive and readable. This ability to "grow the language" to fit the needs of web development is a major reason why Rails remains a favorite for developers who value elegant, "clean" code.

The Basics of Creating Rails Plugins

A Rails Plugin (often distributed as a Gem) is a way to package and share code across multiple Rails applications. Whether you want to add a custom authentication method, a set of UI helpers, or a new database adapter, plugins allow you to "extend" the core functionality of Rails without cluttering your main application logic.

In modern Rails, plugins are almost exclusively built as Rails Engines. An Engine can be thought of as a "miniature application" that provides its own routes, controllers, models, and views to a host application.

Types of Extensions

Depending on your goal, there are three primary ways to extend Rails:

Extension Type Best For Examples
Simple Gem Pure Ruby logic or standalone utilities. nokogiri, httparty
Railtie Deep integration with the Rails boot process (initializers, generators). dotenv-rails, debug
Engine Full-stack features including routes, models, and views. devise, active_admin, thredded
Creating a New Plugin

Rails provides a specialized generator to scaffold a plugin project. Using the --mountable flag creates a "Mountable Engine," which stays isolated in its own namespace to prevent naming conflicts with the host app.

# Generate a new mountable engine named 'blog_engine'
bin/rails plugin new blog_engine --mountable 

This command generates a directory structure that looks remarkably like a standard Rails app, including:

  • lib/: The core Ruby code for your plugin.
  • app/: Models, controllers, and views specific to the plugin.
  • config/routes.rb: Isolated routes for the engine.
  • test/: A "dummy" Rails application used to test the plugin.
Key Component: The Engine Class

The engine.rb file is the heart of your plugin. It inherits from Rails::Engine and allows you to hook into the host application's lifecycle.

 # blog_engine/lib/blog_engine/engine.rb
module BlogEngine
  class Engine < ::Rails::Engine
    isolate_namespace BlogEngine

    # Use an initializer to set up configuration
    initializer "blog_engine.configure_rails_initialization" do |app|
      # Custom setup logic here
    end
  end
end
Integrating with the Host Application

To use your plugin in a real project, you add it to the host's Gemfile and "mount" it in the routes.

1. The Gemfile
gem 'blog_engine', path: 'vendor/plugins/blog_engine' 
2. The Routes
 # config/routes.rb in the Host App
Rails.application.routes.draw do
  # All blog engine routes will now start with /blog
  mount BlogEngine::Engine => "/blog"
end
Best Practices for Plugin Development
  • Namespace Everything: Always wrap your models and controllers in a module (e.g., BlogEngine::Post) to avoid overwriting a host app's Post model.
  • Keep it Modular: Provide configuration options so the host app can choose how the plugin behaves (e.g., changing the default table name).
  • Test Against the Dummy App: Use the generated test/dummy application to ensure your engine works correctly within a real Rails environment.

Note

If you only need to add a single method to a core Rails class (like ActiveRecord::Base), you might not need a full Engine. You can use a ActiveSupport::Concern or a Railtie to inject code during the boot process

Warning: Be careful when overriding core Rails methods in a plugin (Monkey Patching). If Rails updates its internal logic, your plugin might break the host application in unexpected ways.

Rails Engines

A Rails Engine is a subset of the Rails framework that allows you to wrap a full-featured Rails application within another. In fact, every Rails application is essentially a "super engine," with Rails itself providing the core functionality through engines like ActiveRecord and ActionController.

Mountable vs. Full Engines

When creating an engine, you must decide how deeply integrated it should be with the host application.

Feature Full Engine Mountable Engine (Recommended)
Namespacing Shares the host's namespace. Isolated namespace (e.g., Blog::Post).
Routes Merged into the host's routes. Mounted at a specific path (e.g., /blog).
Assets Shared with the host. Isolated CSS/JS manifests.
Best For Extending existing models/logic. Creating modular, plug-and-play features.
Key Characteristics of Mountable Engines

Mountable engines are the most common way to build modular Rails components because they provide Isolation.

  • Isolated Namespace: By calling isolate_namespace in the engine class, the engine’s models and controllers won't conflict with those in the host app. A User model in the engine becomes MyEngine::User.
  • Independent Routing: The engine has its own routes.rb. The host application "mounts" the engine at a specific endpoint, and all internal links remain relative to that endpoint.
  • The Dummy App: Every engine comes with a test/dummy directory. This is a tiny Rails app used to test the engine in a "real-world" scenario during development.
Implementation: Isolation in Action

To ensure your engine doesn't break when used in different apps, you must use specific helpers to link between the engine and the host.

 # To link to a page WITHIN the engine:
# (Uses the engine's internal route helper)
<%= link_to "Engine Home", blog_engine.root_path %>

# To link to a page in the HOST application:
# (Uses the main_app helper)
<%= link_to "Back to Main Site", main_app.root_path %>
Why Use Rails Engines?

Engines are powerful tools for managing large-scale applications (often referred to as Component-Based Rails Architecture or CBRA).

  • Code Reusability: Build a "Forum" engine once and mount it in ten different client apps.
  • Team Isolation: Large teams can work on separate engines (e.g., Billing, Inventory, User Profile) without stepping on each other's code.
  • Faster Testing: You can run tests for a single engine in seconds rather than waiting for a massive monolithic test suite to finish.
  • Gradual Migration: If you are moving toward a microservices architecture, you can first extract logic into an Engine before moving it to a completely separate server.
Common Examples of Engines

Many of the most popular gems in the Rails ecosystem are actually Engines:

  • Devise: A full authentication system with its own views and controllers.
  • Sidekiq Web: The dashboard for monitoring background jobs.
  • Active Admin: A complete administration interface that you mount at /admin.

Warning: While engines provide great isolation, sharing data between the host and the engine (like a User model) can be tricky. It is often best to define a "User" interface or use a specific configuration block to tell the engine which class represents the user in the host app.

Active Support Core Extensions

Active Support is the component of Rails responsible for providing Ruby language extensions, utilities, and other transversal concerns. While most of Rails focuses on web-specific logic (MVC), Active Support enriches the Ruby "Core" (classes like String, Array, and Time) with "sugar" that makes the code more readable and expressive.

This philosophy is often called Monkey Patching—Active Support reopens Ruby’s built-in classes and adds new methods to them.

1. Extensions to Time and Date

This is perhaps the most famous part of Active Support. It allows for natural-language time calculations that read like English sentences.

Method Example Result (if today is Jan 1)
Numeric helpers 2.days, 1.week, 3.hours Returns an ActiveSupport::Duration
Relative time 3.days.from_now Jan 4
Past time 10.minutes.ago 10 minutes before now
Calendrical Date.today.beginning_of_month Jan 1
Advanced 5.years.ago.midnight Jan 1, five years ago at 00:00
2. Extensions to String

Active Support adds powerful transformation methods to strings, which Rails uses internally to convert between class names, table names, and URLs.

  • pluralize / singularize: "person".pluralize ? "people"
  • camelize / underscore: "user_profile".camelize ? "UserProfile"
  • parameterize: "Rails is Awesome!".parameterize ? "rails-is-awesome" (perfect for slugs)
  • truncate: "Very long text...".truncate(10) ? "Very lo..."
  • squish: Removes all surrounding and internal duplicate whitespace.
3. Extensions to Array and Hash

These extensions make data manipulation significantly more concise.

Method Class Description
to_sentence Array ['A', 'B', 'C'].to_sentence"A, B, and C"
in_groups_of Array Splits array into chunks (useful for grid layouts).
except Hash Returns a hash without the specified keys.
stringify_keys Hash Converts all keys to strings.
deep_merge Hash Merges nested hashes recursively.

4. The "Object" Extensions: blank? and present?

In standard Ruby, checking if something exists requires multiple checks (nil?,empty?, etc.). Active Support adds blank? to every object.

  • blank?: Returns true if the object is nil,false, an empty string, an empty array/hash, or a string consisting only of whitespace.
  • present?: The exact opposite of blank?.
  • presence: Returns the object if it's present?, otherwise returns nil.
    • Example: name = params[:name].presence || "Guest"
5. ActiveSupport::Concern

When you want to extract common logic into a module to be shared across multiple models or controllers, Concerns provide a standardized way to handle both instance methods and class methods (like validations or scopes) in one place.

 # app/models/concerns/taggable.rb
module Taggable
  extend ActiveSupport::Concern

  included do
    # Code here runs in the context of the class it's included in
    has_many :tags
    scope :with_tags, -> { joins(:tags) }
  end

  # Instance methods go here
  def tag_list
    tags.map(&:name).join(", ")
  end
end
6. Instrumentation and Notifications

Active Support includes a pub/sub system called ActiveSupport::Notifications. You can "subscribe" to events (like a database query or a controller action) to track performance or log custom data.

 ActiveSupport::Notifications.subscribe "process_action.action_controller" do |*args|
  event = ActiveSupport::Notifications::Event.new(*args)
  puts "Action took #{event.duration}ms"
end

Warning: Be careful when using Active Support in a standalone Ruby script. Because it modifies core classes, it can lead to conflicts if other libraries expect standard Ruby behavior. In a Rails app, however, this is considered idiomatic.

Note

If you are building a small gem and don't want to load all of Active Support, you can cherry-pick specific extensions: require 'active_support/core_ext/object/blank'.

API Reference (Technical Lookup) Last updated: Feb. 28, 2026, 4:22 p.m.

The API Reference serves as the Technical Blueprint for the entire framework. It documents the low-level methods and classes that power everything from routing to database connections. While most developers interact with the high-level "magic" of Rails, the API Reference provides the deep-dive knowledge needed for customization, debugging, and understanding the framework's internal mechanics.

This section is organized by component—Active Record, Action Controller, etc.—providing a standardized lookup for Syntax and Parameters. Whether you are checking the specific options for a database query or looking for a obscure string manipulation method in Active Support, the API Reference is the ultimate source of truth for "How things work under the hood."

ActionController API

ActionController::Base is the heart of the web request cycle. It acts as the "Director," taking data from the router, communicating with models, and rendering views or redirecting. Most of your controllers will inherit from ApplicationController, which in turn inherits from ActionController::Base.

1. Request Handling & Parameters

Controllers interpret incoming data through the params hash, which combines query string parameters, POST data, and route segments.

Method Purpose Example
params Accesses all request parameters. params[:id]
request Accesses the full Request object. request.remote_ip, request.user_agent
headers Accesses or sets HTTP headers. response.headers["X-Frame-Options"] = "SAMEORIGIN"
session Accesses the user's session (encrypted cookies). session[:user_id] = user.id
cookies Manages browser cookies. cookies.signed[:prefs] = "dark-mode"
2. The Filter Lifecycle (Callbacks)

Filters are methods that run before, after, or around a controller action. They are primarily used for authentication, authorization, or setting up data.

  • before_action: Runs before the action. If it renders or redirects, the action is not executed.
  • after_action: Runs after the action. Useful for logging or post-processing headers.
  • around_action: Yields to the action. Useful for wrapping actions in database transactions or error handling.
 class ArticlesController < ApplicationController
  # Only runs 'set_article' for show, edit, update, and destroy
  before_action :set_article, only: [:show, :edit, :update, :destroy]
  
  private

  def set_article
    @article = Article.find(params[:id])
  end
end
3. Rendering and Redirecting

A controller action must eventually result in a response. If you don't call one of these methods, Rails attempts to render a template matching the action name.

Method Behavior Syntax
render Renders a view template or raw data. render :edit, render json: @user
redirect_to Sends a 302 redirect to the browser. redirect_to articles_path, notice: "Saved!"
head Returns an empty response with a status code. head :no_content, head :unauthorized
send_data Sends binary data (like a generated PDF). send_data pdf_blob, filename: "report.pdf"
4. Strong Parameters

To prevent "Mass Assignment" vulnerabilities, Rails requires you to explicitly whitelist which attributes are allowed to be passed to your models.

 def article_params
  # Permits only 'title' and 'body'
  params.require(:article).permit(:title, :body)
end

# Usage:
# Article.create(article_params)
5. Built-in Helpers & Scopes

Action Controller provides several utility methods to manage the flow of data to the view.

  • helper_method: Makes a controller method available to use inside your views.
  • flash: A special part of the session that clears after the next request (used for "Success" or "Error" messages).
  • respond_to: Allows one action to return different formats (HTML, JSON, XML) based on the request.
 def index
  @articles = Article.all
  respond_to do |format|
    format.html # renders index.html.erb
    format.json { render json: @articles }
  end
end
6. Live Streaming

For features like server-sent events or real-time progress bars, you can use ActionController::Live.

 class MyController < ActionController::Base
  include ActionController::Live

  def stream
    response.headers['Content-Type'] = 'text/event-stream'
    10.times do |i|
      response.stream.write "Iteration #{i}\n"
      sleep 1
    end
  ensure
    response.stream.close
  end
end

Warning: Remember that render and redirect_to do not end the execution of your method. They simply set the response. If you have code after these calls, it will still execute unless you explicitly return.

ActiveRecord API

Active Record is the Object-Relational Mapping (ORM) layer of Rails. It translates complex SQL queries into intuitive Ruby methods and handles the persistence of data between your Ruby objects and the relational database.

1. Finder Methods (Querying)

Active Record uses "lazy loading," meaning the database isn't queried until the data is actually needed. This allows you to chain multiple methods together efficiently.

Method SQL Equivalent Description
find(id) WHERE id = ? Finds a record by ID; raises ActiveRecord::RecordNotFound if missing.
find_by(...) WHERE ... LIMIT 1 Finds the first record matching criteria; returns nil if missing.
where(...) WHERE ... Returns an ActiveRecord::Relation (a collection) matching criteria.
order(:col) ORDER BY col Sorts the results (default is ASC).
limit(n) LIMIT n Constrains the number of records returned.
pluck(:col) SELECT col Returns an array of values instead of object instances.
2. Persistence (Saving and Updating)

These methods handle the creation, modification, and deletion of records.

  • save: Saves the model. Returns true on success, false on failure.
  • create: Instantiates and saves a record in one step.
  • destroy: Removes the record from the database and triggers "destroy" callbacks.
  • delete: Removes the record directly from the database (fast, but skips callbacks).
3. Advanced Querying & Aggregates

Active Record provides Ruby wrappers for complex SQL operations.

 # Aggregates
Article.count
Article.average(:word_count)
Article.maximum(:views)

# Chaining with Scopes
# SQL: SELECT * FROM articles WHERE status = 'published' ORDER BY created_at DESC LIMIT 5
Article.published.order(created_at: :desc).limit(5)
4. The "N+1" Solver: Eager Loading

To avoid the common performance pitfall where a query is executed for every child record in a loop, use eager loading.

Method Behavior Use Case
includes Loads associations in separate queries or a JOIN. General-purpose eager loading.
joins Uses an INNER JOIN. Filtering results based on association values.
left_outer_joins Uses a LEFT OUTER JOIN. Including records that may not have an association.
5. Scopes

Scopes allow you to define commonly used queries as class methods inside your model for better readability and reusability.

 class Article < ApplicationRecord
  scope :published, -> { where(published: true) }
  scope :recent, -> { order(created_at: :desc) }
end

# Usage:
@articles = Article.published.recent
6. Calculations and Groups

You can perform complex grouping and counting directly through the API.

 # Returns a Hash: { "Category A" => 5, "Category B" => 12 }
Article.group(:category).count
7. Raw SQL (The Escape Hatch)

When Active Record's API isn't enough, you can still execute raw SQL while maintaining safety against injections.

  • find_by_sql: Returns model instances from a custom string.
  • ActiveRecord::Base.connection.execute: Executes raw SQL and returns a result set (not model objects).

Warning: Always use the hash syntax where(name: val) or the array syntax where("name = ?", val) to prevent SQL Injection. Never interpolate variables directly into a string: where("name = #{val}").

Note

Use find_each instead of each when iterating over thousands of records. find_each batches the records (1,000 at a time) to prevent your application's memory from spiking.

ActiveModel API

Active Model is a library that contains various modules used in Active Record to provide integration with the Rails framework. However, its true power lies in its ability to be used outside of database-backed models. It allows you to create "Plain Old Ruby Objects" (POROs) that behave like Rails models, making them compatible with form helpers, validations, and serialization.

1. Making a Class "Rails-Ready"

By including ActiveModel::Model, your Ruby class gains features like attribute initialization, validations, and conversion for use in form_with.

 class ContactForm
  include ActiveModel::Model
  include ActiveModel::Attributes # For typed attributes

  attribute :name, :string
  attribute :email, :string
  attribute :message, :string

  validates :name, :email, presence: true
  validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }

  def submit
    if valid?
      # Send email logic here
      true
    else
      false
    end
  end
end
2. Core Modules and Their Functions

Active Model is modular; you can include only what you need.

Module Purpose Example Method
ActiveModel::Validations Adds the ability to validate attributes. valid?, errors
ActiveModel::Attributes Adds support for data types (string, integer, etc.). attribute :price, :decimal
ActiveModel::Dirty Tracks changes to attributes before saving. name_changed?, name_was
ActiveModel::Serialization Converts objects to/from JSON or XML. as_json
ActiveModel::Callbacks Provides Active Record-style lifecycle hooks. define_model_callbacks
ActiveModel::Naming Provides routing helpers (e.g., model_name). ContactForm.model_name.human
3. Dirty Tracking (Change Tracking)

This module allows you to see what has changed in an object since it was last "synced." This is useful for audit logs or conditional logic.

 person = Person.new
person.name = "Alice"
person.name_changed? # => true
person.name_change   # => [nil, "Alice"]
person.name_was      # => nil
person.restore_name! # Resets name to previous value
4. Serialization

Active Model provides a standard way to convert your object into a hash for API responses.

 class User
  include ActiveModel::Serialization
  attr_accessor :name, :email

  def attributes
    {'name' => nil, 'email' => nil}
  end
end

user = User.new(name: "Bob", email: "bob@example.com")
user.serializable_hash # => {"name"=>"Bob", "email"=>"bob@example.com"}
5. Attribute Methods

This module allows you to add custom prefixes or suffixes to all your attributes dynamically, similar to how Rails adds _changed?.

class Person
  include ActiveModel::AttributeMethods

  attribute_method_suffix '_verify'
  define_attribute_methods :name

  def name_verify
    # custom logic
  end
end 
6. Errors Object

When validations fail, Active Model populates an errors object. This is a sophisticated collection that handles internationalization (I18n) and message formatting.

  • errors.add(:base, "Message"): Adds a general error not tied to a specific field.
  • errors.full_messages: Returns an array of user-friendly strings (e.g., "Email is invalid").
  • errors[:email]: Returns an array of specific errors for the email field.

Note

Use ActiveModel::Model whenever you have a form that doesn't save directly to a database table (e.g., Search filters, Login forms, or Password resets). This keeps your controllers clean and your views consistent.

Warning: While Active Model provides the behavior of a model, it does not provide persistence. Methods like save or update do not exist unless you define them yourself.

ActiveSupport API

Active Support is the "utility belt" of the Rails framework. While other components focus on the Web or the Database, Active Support focuses on the Ruby language itself. It provides extensions to the Ruby Core and a collection of tools for internationalization, caching, and background processing.

1. Core Extensions (Language Sugar)

Active Support reopens standard Ruby classes to add methods that make code more expressive. These are often called "Core Extensions."

Target Class Popular Methods Example
Object blank?, present?, presence, try user.try(:name)
String camelize, underscore, truncate, pluralize "admin_user".camelize
Array to_sentence, in_groups_of, second, forty_two names.to_sentence
Hash deep_merge, except, stringify_keys, slice params.slice(:id, :name)
Numeric bytes, kilobytes, megabytes 5.megabytes
2. Time & Duration API

Perhaps the most iconic part of Active Support, this API allows for human-readable time manipulation.

  • Duration: Objects like 1.month or 30.minutes that can be added to or subtracted from Time objects.
  • Travel: Methods like 2.days.ago or 1.week.from_now.
  • Calculations: Time.current.beginning_of_year or Date.yesterday.
  • Zones: Time.zone.now (Uses the time zone set in application.rb rather than the server's system time).
3. ActiveSupport::Concern

This module streamlines the "Mix-in" pattern in Ruby. It handles the boilerplate of including instance methods and extending class methods simultaneously, while also managing dependencies between multiple modules.

 module Mailable
  extend ActiveSupport::Concern

  included do
    # Runs in the class context (e.g., validations, scopes)
    validates :email, presence: true
  end

  class_methods do
    # Defines methods on the class itself
    def find_by_email(email)
      find_by(email: email)
    end
  end
end
4. Instrumentation (Notifications)

Active Support provides a internal "Pub/Sub" (Publisher/Subscriber) system. Rails uses this to track how long database queries or controller actions take.

  • instrument: Wraps a block of code to measure it and notify subscribers.
  • subscribe: Listens for specific events to log data or trigger side effects.
 # Subscribing to any SQL query
ActiveSupport::Notifications.subscribe "sql.active_record" do |name, started, finished, unique_id, payload|
  puts "Query: #{payload[:sql]}"
end
5. Caching (ActiveSupport::Cache)

Active Support provides a unified interface for different caching stores (Memory, File, Memcached, Redis).

Store Configuration Example Best Use Case
Memory :memory_store Small, single-process apps.
File :file_store Low-traffic apps with limited RAM.
Redis :redis_cache_store Large, multi-server production environments.
Standard Usage:
 # Fetches the value from cache, or runs the block and saves it if missing
Rails.cache.fetch("expensive_data_key", expires_in: 12.hours) do
  ExpensiveCalculation.run
end
6. JSON & Security Utilities
  • ActiveSupport::JSON: High-performance JSON encoding and decoding.
  • ActiveSupport::MessageVerifier: Generates and verifies signed messages (used for "Remember Me" cookies).
  • ActiveSupport::EncryptedConfiguration: The engine behind the credentials.yml.enc system.
7. Callbacks

Active Support allows you to define your own lifecycle hooks for any Ruby object, similar to before_save in Active Record.

 class Goal
  extend ActiveSupport::Callbacks
  define_model_callbacks :complete

  def complete
    run_callbacks :complete do
      puts "Goal marked as finished!"
    end
  end
end

Note

Active Support is extremely modular. If you are writing a gem or a standalone script, you can load only the extensions you need (e.g., require 'active_support/core_ext/integer/time') to keep the memory footprint low.

Warning: Be wary of Monkey Patching conflicts. If you use Active Support in a project with other libraries that also modify core classes (like Hash or String), ensure their implementations don't overwrite each other.

Railties API

Content goes here...

DocsAllOver

Where knowledge is just a click away ! DocsAllOver is a one-stop-shop for all your software programming needs, from beginner tutorials to advanced documentation

Get In Touch

We'd love to hear from you! Get in touch and let's collaborate on something great

Copyright copyright © Docsallover - Your One Shop Stop For Documentation