Ruby on Rails Zero to Advanced #4: Installing Rails & Your First App
After three parts of pure Ruby, it is time for Rails. In this part we install Rails, generate a new application, walk through the folder structure, understand the MVC pattern, and get our first custom page running on localhost:3000. The examples target Rails 8 with Ruby 3.4, but everything works the same on Rails 7.1+.
Installing Ruby and Rails
On macOS and Linux, avoid the system Ruby. Use a version manager such as rbenv, mise or asdf so each project can pin its own Ruby version. On Windows, use WSL2 and follow the Linux steps. Once a modern Ruby is active, install Rails as a gem:
ruby -v
gem install rails
rails -v
Output:
ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +PRISM [arm64-darwin24]
Fetching rails-8.0.2.gem
...
Successfully installed rails-8.0.2
42 gems installed
Rails 8.0.2
Rails 8 uses SQLite by default, which needs no separate database server, so you can start immediately. You can switch to PostgreSQL or MySQL later.
Creating your first app with rails new
rails new generates a complete, working project: folders, configuration, a Gemfile, a git repository, and it runs bundle install for you.
rails new blog
cd blog
Output (shortened):
create
create README.md
create Rakefile
create .ruby-version
create config.ru
create .gitignore
create Gemfile
run git init from "."
create app
create app/controllers/application_controller.rb
create app/models/application_record.rb
create app/views/layouts/application.html.erb
...
create config/routes.rb
create db/seeds.rb
run bundle install --quiet
run bundle binstubs bundler
rails importmap:install
rails turbo:install stimulus:install
Useful options: rails new blog --database=postgresql to start with PostgreSQL, --css=tailwind for Tailwind CSS, and --api for an API-only app (we will use that idea in part 10).
A tour of the folder structure
Rails favours convention over configuration: every file has an expected place, and Rails finds things by name. These are the folders you will touch most:
| Path | What lives there |
|---|---|
app/models | Model classes (data and business rules), one per table |
app/controllers | Controllers that handle requests and choose a response |
app/views | ERB templates, one folder per controller |
app/jobs, app/mailers | Background jobs and emails |
config/routes.rb | Maps URLs to controller actions |
config/database.yml | Database connection settings per environment |
db/migrate | Migrations that change the database schema |
test | Automated tests (Minitest by default) |
Gemfile | Ruby libraries (gems) your app depends on |
bin/ | Scripts such as bin/rails, bin/dev, bin/setup |
Rails also has three environments: development (your laptop, reloads code on every request), test (used when running tests) and production (the live server). Each has a file in config/environments/.
Understanding MVC
Rails follows the Model-View-Controller pattern. When a browser requests /articles/1:
- The router (
config/routes.rb) matches the URL and picks a controller action, e.g.ArticlesController#show. - The controller asks the model for data:
@article = Article.find(params[:id]). - The model talks to the database through Active Record and returns a Ruby object.
- The controller hands the data to a view (
app/views/articles/show.html.erb), which renders HTML. - The HTML is sent back to the browser.
Keep each layer focused: business rules in models, request handling in controllers, presentation in views. "Skinny controllers, rich models" is a classic Rails guideline.
Running the server
From inside the project folder, start the development server (Puma):
bin/rails server
Output:
=> Booting Puma
=> Rails 8.0.2 application starting in development
=> Run `bin/rails server --help` for more startup options
Puma starting in single mode...
* Puma version: 6.6.0 ("Return to Forever")
* Ruby version: ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +PRISM [arm64-darwin24]
* Min threads: 3
* Max threads: 3
* Environment: development
* PID: 48211
* Listening on http://127.0.0.1:3000
* Listening on http://[::1]:3000
Use Ctrl-C to stop
Open http://localhost:3000 and you will see the Rails welcome page with the Rails and Ruby versions. Stop the server with Ctrl-C. You can also use the short form bin/rails s, or bin/dev if your app uses a CSS/JS build process.
Your first page: controller, route and view
Let's replace the welcome page with our own homepage. Generators create files that follow Rails conventions:
bin/rails generate controller Pages home
Output:
create app/controllers/pages_controller.rb
route get "pages/home"
invoke erb
create app/views/pages
create app/views/pages/home.html.erb
invoke test_unit
create test/controllers/pages_controller_test.rb
invoke helper
create app/helpers/pages_helper.rb
invoke test_unit
Now edit the controller to prepare some data. Instance variables set in an action are automatically available in its view:
# app/controllers/pages_controller.rb
class PagesController < ApplicationController
def home
@app_name = "Blog"
@authors = ["Budi", "Siti"]
end
end
Edit the view. <%= %> outputs a Ruby value into the HTML, while <% %> runs Ruby without printing:
<!-- app/views/pages/home.html.erb -->
<h1>Welcome to <%= @app_name %></h1>
<p>Written by:</p>
<ul>
<% @authors.each do |author| %>
<li><%= author %></li>
<% end %>
</ul>
Finally, make it the homepage in config/routes.rb:
# config/routes.rb
Rails.application.routes.draw do
root "pages#home"
get "pages/home"
end
Restart is not needed in development. Reload http://localhost:3000 and view the page source.
Output (the rendered body, inside the application layout):
<h1>Welcome to Blog</h1>
<p>Written by:</p>
<ul>
<li>Budi</li>
<li>Siti</li>
</ul>
Check which routes exist with bin/rails routes:
bin/rails routes -c pages
Output:
Prefix Verb URI Pattern Controller#Action
root GET / pages#home
pages_home GET /pages/home(.:format) pages#home
The Prefix column gives you URL helpers: root_path returns "/" and pages_home_path returns "/pages/home".
Common mistakes
- Running commands outside the app folder.
bin/rails servermust run insideblog/. Outside it,railsonly offersrails new. - "A server is already running". An old server is still alive. Stop it, or delete a stale
tmp/pids/server.pid. Usebin/rails s -p 3001to pick another port. - Using the system Ruby. Installing gems with
sudointo macOS' system Ruby leads to permission errors. Use a version manager. - Wrong file names.
PagesControllermust be inpages_controller.rb, and thehomeaction rendersapp/views/pages/home.html.erb. Break the convention and Rails cannot find your code. - Forgetting the = in ERB.
<% @app_name %>runs but prints nothing; use<%= @app_name %>.
Exercise
Add an About page at /about. Add an about action to PagesController, create app/views/pages/about.html.erb, and add this route:
get "about", to: "pages#about", as: :about
Then link to it from the homepage with <%= link_to "About us", about_path %>. The rendered link should be:
Output:
<a href="/about">About us</a>
Summary
You installed Rails, created an app with rails new, learned where things live, saw how a request flows through routes, controllers, models and views, and built your first page with a controller, an ERB view and a root route. In part 5 we go deeper into routing, controllers and views: resources, params, layouts and partials.
Related articles
PostgreSQL Zero to Advanced #1: Introduction & Setup
Start the series by understanding what a relational database is, installing PostgreSQL locally, and getting comfortable with psql commands like \l, \c and \dt.
PHP Zero to Advanced #1: Introduction & Setup
Start your PHP journey: what PHP is, how to install PHP 8.2+, run the built-in development server, and write your very first script with echo.
Python Zero to Advanced #1: Introduction & Setup
Start your Python journey: what Python is, how to install it, how to use the REPL, print your first output, and run .py files from the terminal.