Skip to content

Ruby on Rails - Quick Guide

Before diving into Rails, it’s essential to understand Ruby, the programming language Rails is built upon.

Ruby is often described as a blend of:

  • Smalltalk’s conceptual elegance and pure object-orientation,
  • Python’s ease of use and readability, and
  • Perl’s pragmatic scripting capabilities.

Key characteristics of Ruby:

  • A dynamic, open-source programming language with a focus on simplicity and productivity.
  • Interpreted, similar to Perl, Python, and Tcl/TK.
  • Truly object-oriented: In Ruby, everything is an object, including primitive types like numbers.

Ruby’s design philosophy and features make it an excellent fit for web development with Rails. Its popularity stems from:

  • Readability and Conciseness: Ruby’s syntax is designed to be close to natural language, making it relatively easy to learn and read.
  • Open Source: Available under a liberal license, fostering a large and active community.
  • Rich Standard Library and Gems: A vast collection of libraries (called ‘gems’) for almost any task.
  • Metaprogramming: Ruby allows programs to write or manipulate other programs (or themselves), which Rails heavily utilizes to reduce boilerplate code.
  • Developer Happiness: A core tenet of the Ruby community, focusing on making development enjoyable and productive.

Considerations:

  • Performance: Historically, Ruby’s execution speed was a concern compared to compiled languages. However, with advancements like YJIT (Yet Another JIT Compiler) in recent Ruby versions, performance has significantly improved for Rails applications.
  • Concurrency and Parallelism: Ruby’s Global Interpreter Lock (GIL) in MRI (Matz’s Ruby Interpreter) means only one thread can execute Ruby code at a time per process. However, I/O-bound operations can release the GIL, allowing for concurrency. For CPU-bound tasks, multi-process architectures (like those used by Puma web server) or experimental features like Ractors offer parallelism.

Here’s a simple Ruby class:

# The Greeter Class
class Greeter
def initialize(name)
@name = name.capitalize
end
def salute
puts "Hello, #{@name}! Welcome to the world of Ruby."
end
end
# Create a new object
g = Greeter.new("rubyist")
# Call the salute method
g.salute

Output:

Hello, Rubyist! Welcome to the world of Ruby.

ERB (Embedded Ruby) is a templating system that allows you to embed Ruby code within text documents, most commonly HTML for web pages. Rails uses ERB extensively for its view templates.

ERB uses specific tags:

  • <% ... %>: Executes the Ruby code within the tags. Nothing is printed to the output.
  • <%= ... %>: Executes the Ruby code and prints the result of the expression to the output.
  • <%# ... %>: A comment tag; the code within is ignored.

Example (template.html.erb):

<% page_title = "ERB Demonstration" %>
<% salutation = "Dear Developer," %>
<html>
<head>
<title><%= page_title %></title>
</head>
<body>
<p><%= salutation %></p>
<p>This is an example of how ERB templates work in Rails views.</p>
</body>
</html>

When processed by Rails, this ERB template would generate HTML with the Ruby variable values interpolated.

  • Ruby on Rails (often just “Rails”) is a powerful web application framework written in Ruby.
  • Created by David Heinemeier Hansson (DHH).
  • It follows the Model-View-Controller (MVC) architectural pattern.
  • Emphasizes Convention over Configuration (CoC) and Don’t Repeat Yourself (DRY) principles.
  • Designed for rapid development of database-backed web applications.
  • Open source and has a large, vibrant community.

Rails is a full-stack framework, meaning it provides tools and libraries for all layers of web application development:

  • Database interaction (via Active Record ORM).
  • Routing (mapping URLs to controller actions).
  • Controller logic (handling requests and responses).
  • View templating (generating HTML with ERB, Slim, Haml, etc.).
  • Asset management (CSS, JavaScript, images via Sprockets or Webpacker/Shakapacker).
  • Background jobs (Active Job).
  • Real-time features (Action Cable).
  • Email sending (Action Mailer).

Rails makes assumptions about common naming conventions (e.g., a Book model maps to a books database table). By following these conventions, developers write significantly less configuration code compared to many other frameworks. The application’s structure and database schema often provide Rails with all the information it needs.

Rails offers features that boost developer productivity:

Rails leverages Ruby’s metaprogramming capabilities to dynamically define methods and behaviors, reducing boilerplate and making code more expressive. For example, Active Record automatically creates accessor methods for database columns.

The Object-Relational Mapper (ORM) in Rails. It allows developers to interact with database tables as if they were Ruby objects, abstracting away much of the SQL. Active Record handles database connections, schema discovery, CRUD operations, associations, validations, and more.

Rails has a strong testing culture. It automatically generates test files (using Minitest by default) when you create models or controllers. Tools like fixtures or factories (e.g., FactoryBot) help set up test data. RSpec is a popular alternative testing framework.

Rails provides three default environments: development, test, and production. Each has slightly different configurations (e.g., caching behavior, error reporting, database used) tailored to its purpose, streamlining the development lifecycle.

Rails can generate ‘scaffolding’ – a basic set of model, views, controller, and routes for a resource. This is useful for quickly getting a CRUD interface up and running, for prototyping, or for learning how Rails components fit together.

To develop web applications with Ruby on Rails, you need to set up your development environment with the following components:

  • Ruby: The programming language.
  • RubyGems: The package manager for Ruby libraries (gems).
  • Bundler: Manages application-specific gem dependencies.
  • Rails: The framework itself (installed as a gem).
  • A JavaScript Runtime: Such as Node.js, often required for the asset pipeline (managing JavaScript and CSS).
  • A Database System: Rails supports SQLite, PostgreSQL, MySQL, Oracle, SQL Server, etc. SQLite is often the default for new applications in development and test environments.
  • A Web Server: Puma is the default web server for Rails applications.

We’ll cover installation steps for Windows and Linux-based systems. For macOS, the Linux steps using a version manager like rbenv or RVM are generally recommended.

General Recommendation: Use a Ruby Version Manager

Section titled “General Recommendation: Use a Ruby Version Manager”

Using a Ruby version manager like rbenv or RVM (Ruby Version Manager) is highly recommended on macOS and Linux. It allows you to easily install and switch between different Ruby versions without conflicts, which is crucial as projects may require specific Ruby versions.

Follow these steps to install Ruby on Rails on Windows:

  1. Go to RubyInstaller for Windows.
  2. Download the recommended Ruby+Devkit installer version (e.g., Ruby 3.2.x (x64) or the latest stable version).
  3. Run the installer. Accept the license and keep the default settings, ensuring “Add Ruby executables to your PATH” is checked.
  4. On the “Installation Destination and Optional Tasks” screen, also check the MSYS2 development toolchain installation option (usually the first option, e.g., ridk install). This is important for installing gems with native extensions.
  5. After Ruby installation finishes, a command prompt window might open for the MSYS2 setup. Press ENTER to install the MSYS2 base system and development toolchain if prompted.

Step 2: Verify Ruby and RubyGems Installation

Section titled “Step 2: Verify Ruby and RubyGems Installation”

Open a new Command Prompt (not PowerShell initially, as PATH updates might not be immediate) and type:

C:\> ruby -v
C:\> gem -v

You should see the installed Ruby and RubyGems versions.

Bundler manages your application’s gem dependencies. Rails itself is a gem.

C:\> gem install bundler
C:\> gem install rails

This might take a few minutes as it downloads and installs Rails and its dependencies. Ensure you have an active internet connection.

Check the installed Rails version:

C:\> rails -v

This should output the Rails version, e.g., Rails 7.1.0.

Step 5: Install Node.js (JavaScript Runtime)

Section titled “Step 5: Install Node.js (JavaScript Runtime)”

Rails often requires a JavaScript runtime for features like the Asset Pipeline. Install Node.js from nodejs.org. Download the LTS (Long Term Support) version.

Using rbenv is a popular method for managing Ruby versions on Linux.

Step 1: Install rbenv and Ruby Build Plugin

Section titled “Step 1: Install rbenv and Ruby Build Plugin”

First, install dependencies for building Ruby (commands may vary slightly by Linux distribution, this is for Debian/Ubuntu):

$ sudo apt update
$ sudo apt install -y git curl autoconf bison build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev libgdbm-dev libjemalloc-dev

Install rbenv and ruby-build:

$ git clone https://github.com/rbenv/rbenv.git ~/.rbenv
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc # Or ~/.zshrc if using Zsh
$ echo 'eval "$(rbenv init -)"' >> ~/.bashrc
$ exec $SHELL
$ git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-build

Close and reopen your terminal or run exec $SHELL to apply changes.

List available Ruby versions: rbenv install -l. Choose a recent stable version (e.g., 3.2.2):

$ rbenv install 3.2.2 # This can take a while
$ rbenv global 3.2.2 # Sets this as the default Ruby version

Verify Ruby installation:

$ ruby -v

Disable gem documentation generation (optional, saves time):

$ echo "gem: --no-document" > ~/.gemrc
$ gem install bundler
$ gem install rails
$ rbenv rehash # Important after installing gems that provide commands
$ rails -v

Install Node.js. Using NodeSource repositories is a common way:

# For Node.js LTS (e.g., version 20.x)
$ curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
$ sudo apt-get install -y nodejs

Step 6: Install a Database (Example: PostgreSQL)

Section titled “Step 6: Install a Database (Example: PostgreSQL)”

While SQLite works out of the box, PostgreSQL is a robust choice for production and often development.

$ sudo apt install postgresql postgresql-contrib libpq-dev
# Start and enable PostgreSQL (systemd)
$ sudo systemctl start postgresql
$ sudo systemctl enable postgresql
# Create a database user (you might need to switch to 'postgres' user first)
$ sudo -u postgres createuser --interactive --pwprompt
# Enter name of role to add: your_username
# Enter password for new role: your_password
# Make it a superuser? (y/n) y (for development ease, or grant specific privileges later)

To update Rails to the latest version (or a specific version within your Gemfile constraints):

# To update the Rails gem itself system-wide (or for rbenv/RVM current Ruby)
$ gem update rails
# To update Rails for a specific project (preferred):
# Edit your Gemfile to specify the desired Rails version, then run:
$ bundle update rails

Let’s create a new Rails application to verify the installation:

$ rails new demo_app
$ cd demo_app
$ bin/rails server

Open your browser and go to http://localhost:3000. You should see the Rails default welcome page.

The welcome page typically displays information about your Rails and Ruby versions, and links to guides. This confirms your environment is set up correctly.

A web framework provides a standard way to build and deploy web applications. Ruby on Rails is a full-stack framework that simplifies common web development tasks by providing structure, tools, and conventions.

When developing a Rails application, you typically focus on three primary tasks, which align with the Model-View-Controller (MVC) pattern:

  • Describe and model your application’s domain: This involves defining the entities (e.g., users, products, articles), their attributes, and their relationships. This is primarily handled by Rails Models (Active Record).
  • Specify what can happen in this domain: Define the actions and business logic. For example, users can register, products can be added to a cart, articles can be published. This is handled by Rails Controllers (Action Controller).
  • Choose and design the publicly available views of the domain: Determine how information is presented to the user and how users interact with the application (e.g., forms, lists, show pages). This is handled by Rails Views (Action View).

The Model-View-Controller (MVC) pattern is an architectural pattern that separates an application into three interconnected components to manage complexity and promote modularity.

The Model represents the application’s data and business logic. In Rails, this is primarily handled by Active Record.

  • Object-Relational Mapping (ORM): Active Record maps database tables to Ruby classes, rows to objects, and columns to object attributes.
  • Data Persistence: Handles saving, retrieving, updating, and deleting data from the database.
  • Associations: Manages relationships between different models (e.g., a User has_many Posts).
  • Validations: Enforces data integrity rules (e.g., an email must be present and unique).
  • Callbacks: Allows custom logic to be executed at different points in an object’s lifecycle (e.g., before saving, after destroying).

The View is responsible for presenting data to the user and handling user input. In Rails, this is handled by Action View.

  • Templating: Uses template engines like ERB (Embedded Ruby), Haml, or Slim to generate HTML (or other formats like JSON, XML).
  • Helpers: Provides helper methods to simplify common view tasks, such as generating forms, links, formatting dates, and numbers.
  • Partials: Allows reusable view snippets to be shared across different views.
  • Layouts: Defines the common structure (header, footer, navigation) that wraps individual views.

The Controller acts as an intermediary between the Model and the View. It receives requests from the user (via the router), interacts with the Model to fetch or manipulate data, and then selects a View to render the response.

  • Request Handling: Processes incoming HTTP requests (GET, POST, PATCH, DELETE, etc.).
  • Action Methods: Public methods within a controller class that correspond to specific requests.
  • Data Orchestration: Fetches data from models and prepares it (e.g., @user = User.find(params[:id])) for the view.
  • Session Management: Handles user sessions and cookies.
  • Parameters: Accesses data submitted by the user (e.g., form data, URL parameters) via the params object (with Strong Parameters for security).
  • Rendering and Redirection: Decides whether to render a view template or redirect the user to another URL.
  1. An HTTP request from a user’s browser reaches the Rails Router.
  2. The Router maps the URL to a specific Controller action.
  3. The Controller action is executed. It might interact with one or more Models to retrieve or modify data.
  4. The Model communicates with the database.
  5. The Controller action receives data back from the Model and makes it available to the View (typically via instance variables like @users).
  6. The Controller then renders a View template.
  7. The View template (e.g., an ERB file) generates an HTML response, possibly using data provided by the Controller and helper methods.
  8. The HTML response is sent back to the user’s browser.

This separation of concerns makes Rails applications easier to understand, maintain, and test. Each component has a clear responsibility.

In a standard Rails application, you’ll find these components in the app/ directory:

  • app/models/: Contains your Active Record model files (e.g., user.rb, article.rb).
  • app/views/: Contains subdirectories for each controller, holding their respective view templates (e.g., app/views/users/index.html.erb). Layouts are in app/views/layouts/.
  • app/controllers/: Contains your Action Controller files (e.g., users_controller.rb, articles_controller.rb). application_controller.rb is the base controller.

The core libraries (Active Record, Action View, Action Controller, etc.) are gems managed by Bundler. You can inspect their source code if needed, often found by running bundle show <gem_name> in your project directory.

When you create a new Rails application using the rails new app_name command, Rails generates a standardized directory structure. This consistency is a key aspect of “Convention over Configuration,” making it easier for developers to navigate and understand different Rails projects.

Let’s examine the top-level directory structure of a typical Rails application (e.g., one named my_app):

$ rails new my_app
$ cd my_app
$ ls -F # Or 'dir' on Windows

You will find the following main directories and files:

  • app/: The heart of your application. It contains the core code: models, views, controllers, helpers, mailers, jobs, channels, and assets.
    • assets/: For static assets like stylesheets (app/assets/stylesheets/), JavaScript files (app/assets/javascripts/ if using Sprockets, or managed by app/javascript/ with tools like esbuild/Webpacker), and images (app/assets/images/).
    • channels/: For Action Cable WebSocket communication.
    • controllers/: Contains controller classes. application_controller.rb is the base for all other controllers.
    • helpers/: Helper modules that provide utility methods, often for views. application_helper.rb is a base.
    • javascript/: (More common in modern Rails) For JavaScript code, often managed by tools like esbuild, Webpacker/Shakapacker, or import maps.
    • jobs/: For background jobs using Active Job (e.g., application_job.rb).
    • mailers/: For Action Mailer classes that handle sending emails (e.g., application_mailer.rb).
    • models/: Contains Active Record model classes (e.g., application_record.rb is the base).
    • views/: Holds view templates, typically organized into subdirectories named after controllers. app/views/layouts/ contains layout files.
  • bin/: Contains executable scripts for your application, like rails (for running Rails commands like rails console, rails server), bundle (for Bundler), rake (for Rake tasks), and setup (for initial app setup).
  • config/: Configuration files for your application.
    • application.rb: Main configuration for your application.
    • database.yml: Database connection settings for different environments.
    • environment.rb: Initializes the Rails application.
    • environments/: Environment-specific configurations (e.g., development.rb, test.rb, production.rb).
    • initializers/: Ruby files run on application startup for setting up gems or custom configurations.
    • locales/: Internationalization (I18n) files for translations (e.g., en.yml).
    • master.key or credentials.yml.enc: For storing encrypted credentials (API keys, secrets). config/credentials/development.key, etc. in newer Rails.
    • puma.rb: Configuration for the Puma web server.
    • routes.rb: Defines the application’s URL routing.
    • storage.yml: Configuration for Active Storage (file uploads).
  • db/: Database-related files.
    • migrate/: Contains database migration files that define changes to your database schema.
    • schema.rb or structure.sql: Represents the current state of your database schema.
    • seeds.rb: Ruby code to populate your database with initial data.
  • lib/: Custom library modules or code that doesn’t fit neatly into app/.
    • assets/: For library-specific assets.
    • tasks/: Custom Rake tasks.
  • log/: Log files for different environments (e.g., development.log, production.log).
  • public/: Publicly accessible files, like error pages (404.html, 500.html), favicon.ico, and robots.txt. Files here are served directly by the web server.
  • storage/: (If using Active Storage with the disk service) Where uploaded files are stored locally.
  • test/: Test files (using Minitest by default). Includes directories for fixtures, system tests, integration tests, models tests, controller tests, etc.
  • tmp/: Temporary files (cache, pids, sockets).
  • vendor/: Third-party code not managed by RubyGems (less common now). Often used for JavaScript libraries if not managed via app/javascript or package managers.
  • .gitattributes, .gitignore: Git configuration files.
  • .ruby-version: Often used by Ruby version managers (like rbenv, RVM) to specify the Ruby version for the project.
  • Gemfile: Lists the gem dependencies for your application, managed by Bundler.
  • Gemfile.lock: Records the exact versions of all gems used, ensuring consistent environments.
  • package.json, yarn.lock or package-lock.json: (If using Node.js for JavaScript management) Node.js package manager files.
  • Rakefile: Defines Rake tasks for your application.
  • README.md: A Markdown file with information about your application.
  • config.ru: A Rack configuration file used by Rack-based servers (like Puma) to start the application.

Understanding this structure is fundamental to working effectively with Rails. While it might seem like many directories at first, their clear purposes make Rails projects predictable and maintainable.

Before your Rails application can store and retrieve data, you need to set up a database and configure Rails to connect to it. Rails follows a convention of having separate databases for different environments: development, test, and production.

For a project named library_system, the conventional database names would be:

  • library_system_development
  • library_system_test
  • library_system_production

You should create these databases in your chosen database system (e.g., PostgreSQL, MySQL, or use the default SQLite which requires less manual setup). You’ll also typically need a database user with appropriate privileges.

  1. Create Databases: Connect to your MySQL server (e.g., using the mysql command-line client).
mysql> CREATE DATABASE library_system_development CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Query OK, 1 row affected (0.01 sec)
mysql> CREATE DATABASE library_system_test CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Query OK, 1 row affected (0.00 sec)
mysql> CREATE DATABASE library_system_production CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Query OK, 1 row affected (0.00 sec)
  1. Create a User and Grant Privileges (Replace 'your_user' and 'your_password' with secure credentials):
mysql> CREATE USER 'library_user'@'localhost' IDENTIFIED BY 'secure_password';
Query OK, 0 rows affected (0.00 sec)
mysql> GRANT ALL PRIVILEGES ON library_system_development.* TO 'library_user'@'localhost';
mysql> GRANT ALL PRIVILEGES ON library_system_test.* TO 'library_user'@'localhost';
mysql> GRANT ALL PRIVILEGES ON library_system_production.* TO 'library_user'@'localhost';
Query OK, 0 rows affected (0.00 sec)
mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)

Edit config/database.yml in your Rails application. Update the MySQL sections:

default: &default
adapter: mysql2
encoding: utf8mb4
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: library_user
password: <%= ENV['LIBRARY_DATABASE_PASSWORD'] || 'secure_password' %> # Use ENV var in production
host: localhost
# socket: /tmp/mysql.sock # Optional, if not using default socket
development:
<<: *default
database: library_system_development
test:
<<: *default
database: library_system_test
production:
<<: *default
database: library_system_production
username: prod_library_user # Potentially different user/pass for production
password: <%= ENV['LIBRARY_PROD_DATABASE_PASSWORD'] %>

Important: For passwords, especially in production, use environment variables (e.g., ENV['LIBRARY_DATABASE_PASSWORD']) instead of hardcoding them. Rails credentials can also be used.

  1. Create a User (if one doesn’t exist): Connect to PostgreSQL (e.g., psql postgres), or use createuser from the command line.
$ sudo -u postgres createuser --interactive --pwprompt
# Enter name of role to add: library_user
# Enter password for new role: secure_password
# Shall the new role be a superuser? (y/n) n (unless necessary)
# Shall the new role be allowed to create databases? (y/n) y
# Shall the new role be allowed to create more new roles? (y/n) n
  1. Create Databases:
$ sudo -u postgres createdb -O library_user library_system_development
$ sudo -u postgres createdb -O library_user library_system_test
$ sudo -u postgres createdb -O library_user library_system_production

Alternatively, from within psql as the postgres user or another superuser:

CREATE DATABASE library_system_development OWNER library_user;
CREATE DATABASE library_system_test OWNER library_user;
CREATE DATABASE library_system_production OWNER library_user;

Edit config/database.yml:

default: &default
adapter: postgresql
encoding: unicode
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: library_user
password: <%= ENV['LIBRARY_DATABASE_PASSWORD'] || 'secure_password' %> # Use ENV var in production
host: localhost
# port: 5432 # Optional, if not default
development:
<<: *default
database: library_system_development
test:
<<: *default
database: library_system_test
production:
<<: *default
database: library_system_production
username: prod_library_user # Potentially different user/pass for production
password: <%= ENV['LIBRARY_PROD_DATABASE_PASSWORD'] %>

If you’re using SQLite (the default for new Rails apps if no database is specified with -d), setup is simpler as SQLite databases are just files. Rails will typically create these files (e.g., db/development.sqlite3) automatically when needed (e.g., when running migrations).

The default config/database.yml for SQLite looks like this:

default: &default
adapter: sqlite3
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
timeout: 5000
development:
<<: *default
database: storage/development.sqlite3 # Or db/development.sqlite3 in older Rails
test:
<<: *default
database: storage/test.sqlite3 # Or db/test.sqlite3
production:
<<: *default
database: storage/production.sqlite3 # Or db/production.sqlite3

Once config/database.yml is set up (for PostgreSQL/MySQL), you can often use Rails commands to create the databases (if your database user has permission):

$ bin/rails db:create # Creates development and test databases
$ bin/rails db:create RAILS_ENV=production # Creates production database (use with caution)

This command reads database.yml and attempts to create the databases. For SQLite, this command usually isn’t necessary as the files are created on demand.

With the database configured, the next steps involve defining your application’s data structure using Active Record models and then creating or modifying the database schema with migrations.