Rails controllers and views
rails new
Rails is a gem and a generator. rails new writes a whole project: a Gemfile, an application class, a config folder and a bin/ with the commands. The generator pulls a large dependency tree, so these commands ran on the host in the ruby:3.4 image:
gem install rails
31 gems installed
rails new demo
... create db/cable_schema.rb force config/cable.yml
cd demo
The new project has a database configured, a test setup and a development server. bin/rails about prints the versions, which is the first command to run when a tutorial and the installed Rails disagree.
A resource route
A scaffold generates the model, the controller, the views and the route for one resource. The route is one line in config/routes.rb:
Rails.application.routes.draw do
resources :todos
end
bin/rails generate scaffold Todo title:string done:boolean
invoke active_record create db/migrate/20261105004223_create_todos.rb create app/models/todo.rb invoke test_unit create test/models/todo_test.rb create test/fixtures/todos.yml invoke resource_route route resources :todos invoke scaffold_controller create app/controllers/todos_controller.rb invoke erb create app/views/todos create app/views/todos/index.html.erb create app/views/todos/edit.html.erb create app/views/todos/show.html.erb create app/views/todos/new.html.erb create app/views/todos/_form.html.erb create app/views/todos/_todo.html.erb invoke resource_route invoke test_unit create test/controllers/todos_controller_test.rb invoke helper create app/helpers/todos_helper.rb invoke test_unit invoke jbuilder create app/views/todos/index.json.jbuilder create app/views/todos/show.json.jbuilder create app/views/todos/_todo.json.jbuilder
bin/rails db:migrate
== 20261105004223 CreateTodos: migrating ====================================== -- create_table(:todos) -> 0.0035s == 20261105004223 CreateTodos: migrated (0.0035s) =============================
The generated controller at app/controllers/todos_controller.rb handles all seven actions, and the generated views are the five files above plus two partials. The controller and view in the next two sections are simplified versions of those files, kept short enough to read; the output on this page is from the generated scaffold.
resources :todos expands to seven routes: index, show, new, create, edit, update and destroy. They share a controller and a set of URL helpers, which is the convention that keeps a Rails application predictable.
The controller
The generated controller has one method per action. The ones a reader meets first are index, show and create:
class TodosController < ApplicationController
def index
@todos = Todo.order(:created_at)
end
def show
@todo = Todo.find(params[:id])
end
def create
@todo = Todo.new(todo_params)
if @todo.save
redirect_to @todo, notice: "Todo was created."
else
render :new, status: :unprocessable_entity
end
end
private
def todo_params
params.require(:todo).permit(:title, :done)
end
end
params holds the path, the query and the form body. permit is the allow list: a parameter that is not listed never reaches the model, which is the protection against a form that posts an extra admin: true.
The view
The view is ERB: HTML with Ruby inside <% %>. The output tag is <%= %>, and the instance variable the controller set is available:
<h1>Todos</h1>
<ul>
<% @todos.each do |todo| %>
<li><%= link_to todo.title, todo %></li>
<% end %>
</ul>
<%= link_to "New todo", new_todo_path %>
link_to builds the path from the route helpers, so a change to the route changes every link. The generated views are a starting point: a real page replaces the markup and keeps the helpers.
bin/rails server
=> Booting Puma => Rails 8.1.4.1 application starting in development => Run `bin/rails server --help` for more startup options Puma starting in single mode... * Puma version: 8.0.2 ("Into the Arena") * Ruby version: ruby 3.4.12 (2026-10-23 revision 366f5fdc00) +PRISM [x86_64-linux] * Min threads: 3 * Max threads: 3
The server listens on 3000. The index page the scaffold wrote renders the heading and an empty list, because the table is empty:
curl -s localhost:3000/todos
<h1>Todos</h1> <div id="todos"> </div>
The development log prints the SQL, the template and the timing of each request, which is the first place to look when a page is slow.
What to watch for
Rails is convention over configuration: a file in the wrong folder is
loaded late or not at all. The error usually names the constant and the
folder the framework looked in.
A scaffold is a teaching tool, not a design. It writes views and
controller actions a real app replaces within a week; keep the migration,
the model and the routes, and rewrite the rest.
Next: A JSON API in Rails
Article author: Arthur Isaev