Getting Started with Rails
Rails ships a whole application skeleton so you spend the first hour on the request path, not on inventing folder names.
<!-- hal:authoritative:yaml -->
Rails ships a whole application skeleton so you spend the first hour on the request path, not on inventing folder names.
§I — Frame
Duha session 01 for the Rails track. First dual-fire beside Rust. The spine is the official Rails Guides snapshot on disk: rails-guides/ at v8.1.3.1. Today's file is getting_started.html. Flanagan stays on the shelf until Ruby syntax itself blocks a Guides step.
Rails is a web framework written in Ruby. The Guides open with two design rules that shape every later chapter:
- Don't Repeat Yourself (DRY) — one authoritative place for each fact the system knows.
- Convention Over Configuration — defaults for the common case, so you configure the exceptions.
If you fight those defaults with habits from other stacks, the framework feels hostile. If you learn the conventions, generators and autoloading do the boring work.
By the end, create a Rails 8.1 app, generate a Product model, migrate, perform create/read/update/delete in the console, and state how a request reaches a view through a route and a controller action. Later sessions deepen Active Record, routing, and controllers on their own Guides pages.
§II — Create the app and read the tree
Prerequisites from the guide: Ruby 3.2+, Rails 8.1.0+, a code editor. Confirm the framework version:
rails --version
# Rails 8.1.0 or higher
Generate the sample store application:
rails new store
cd store
rails new writes the foundation. The directories that matter on day one:
| Path | Role |
|---|---|
app/ | Controllers, models, views, jobs, assets — most daily work |
bin/ | rails and setup scripts pinned to this app |
config/ | Routes, database, environment |
db/ | Schema and migrations |
Gemfile | Gem dependencies for Bundler |
test/ | Tests and fixtures |
Inside an application directory, prefer bin/rails so you invoke this app's Rails, not some other install on the PATH.
§III — MVC is the map
Rails organizes application code as Model-View-Controller:
- Model — data and rules. Usually a database table via Active Record.
- View — response rendering (HTML, JSON, and other formats).
- Controller — accepts the request, runs logic, prepares data for the view.
Almost every Getting Started step places code in one of those three homes under app/. When you are lost, ask which of the three owns the change.
§IV — Hello, Rails
Create the database and boot the server:
bin/rails db:create
bin/rails server
Puma listens on http://127.0.0.1:3000. The welcome page is the smoke test that the stack can serve a response. Stop with Ctrl-C.
In development, Rails reloads changed files so you rarely restart the server for ordinary edits. Naming conventions replace most manual require calls; see the Autoloading guide when a constant fails to load.
§V — Model, migration, table
Active Record maps tables to Ruby classes and writes SQL for ordinary create, read, update, and delete work. Default database for a new app is SQLite.
Generate a product with a string name:
bin/rails generate model Product name:string
That command creates:
- a migration under
db/migrate/ app/models/product.rb- model tests and fixtures
Model class names are singular (Product). Table names are plural (products). The migration for Rails 8.1 looks like:
# db/migrate/<timestamp>_create_products.rb
class CreateProducts < ActiveRecord::Migration[8.1]
def change
create_table :products do |t|
t.string :name
t.timestamps
end
end
end
t.timestamps adds created_at and updated_at. Apply pending migrations:
bin/rails db:migrate
bin/rails db:rollback undoes the last migration when you need to reverse a local mistake.
The model file starts empty on purpose:
# app/models/product.rb
class Product < ApplicationRecord
end
Rails reads column names and types from the table and defines attributes at runtime. Product.column_names in the console returns id, name, created_at, and updated_at after migrate.
§VI — Console CRUD
The console is an interactive Ruby shell inside the app:
bin/rails console
Create:
product = Product.new(name: "T-Shirt")
product.save
# or
Product.create(name: "Pants")
save and create run validations, then INSERT. Query:
Product.all
Product.where(name: "Pants")
Product.order(name: :asc)
Product.find(1)
all / where / order return an ActiveRecord::Relation. find returns one record (or raises if missing).
Update:
product = Product.find(1)
product.update(name: "Shoes")
# or assign then save
product.name = "T-Shirt"
product.save
Delete:
product.destroy
Add a presence validation so blank names fail:
class Product < ApplicationRecord
validates :name, presence: true
end
If the console was already open, run reload!. Then:
product = Product.new
product.save # => false
product.errors.full_messages
# => ["Name can't be blank"]
Validations run on create, update, and save. That is enough database discipline for session 01. Active Record Basics and Validations Guides deepen the same surface later.
§VII — A request's journey
To serve HTML beyond the welcome page you need three pieces:
- Route — maps a URL and HTTP verb to a controller action (Ruby DSL in
config/routes.rb). - Controller action — a public method on a controller class under
app/controllers/. - View — a template, usually HTML mixed with Ruby, under
app/views/.
HTTP methods tell the server what to do: GET reads, POST creates, PATCH/PUT update, DELETE destroys. A route pairs a method and path with controller#action:
# config/routes.rb
Rails.application.routes.draw do
get "/products", to: "products#index"
resources :products
root "products#index"
end
resources :products expands to the usual eight CRUD routes (index, new, create, show, edit, update×2, destroy). bin/rails routes prints the table. Generate a controller with an index action (routes already declared, so skip route generation):
bin/rails generate controller Products index --skip-routes
# app/controllers/products_controller.rb
class ProductsController < ApplicationController
def index
@products = Product.all
end
end
Empty actions still render the matching template. Instance variables (names starting with @) are how actions share data with views. The index action renders app/views/products/index.html.erb:
<%# app/views/products/index.html.erb %>
<h1>Products</h1>
<%= debug @products %>
<%= %> evaluates Ruby and prints the result. Visit http://localhost:3000/products (or / if root points at products#index) and the YAML dump of @products appears. Replace debug with real markup in later fires; here the point is the path: request → route → action → model query → view.
When a URL 404s, check the route. When data is wrong, check the model and the action. When markup is wrong, check the view. Session 08 (Routing) and session 09 (Action Controller) deepen each layer on their own Guides pages.
§VIII — One complete proof
Run this checklist on a fresh shell against the store app:
rails new store && cd store
bin/rails db:create
bin/rails generate model Product name:string
bin/rails db:migrate
bin/rails console
Inside the console:
Product.create!(name: "T-Shirt")
Product.create!(name: "Pants")
Product.where(name: "Pants").count # => 1
p = Product.find_by(name: "T-Shirt")
p.update!(name: "Shoes")
Product.pluck(:name) # => ["Shoes", "Pants"]
p.destroy
Product.count # => 1
Exit the console. Add resources :products and root "products#index", generate ProductsController#index, set @products = Product.all, and open / in the browser. You should see the remaining product via debug.
You do not need the full store UI in this session. You need the map, a working model, and one request that proves route → action → view.
That meets session 01: app created, model migrated, console CRUD proven, request path named and exercised once.
§IX — Closing
Rails assumes DRY and Convention Over Configuration. rails new builds the tree. MVC places nearly every change. Active Record turns tables into classes; migrations change the schema in replayable steps; the console proves CRUD before you wire HTML. A browser request still needs route, controller action, and view.
Name the hinge when Product gains a presence validation and save returns false: the database edge started enforcing product shape in Ruby before any form existed.
Session 02 is Guides Active Record Basics. Stop here.
Examine well. Generate the model, migrate, create two records, filter with where, update one name, destroy one row, and write the three MVC folders you would touch to show the remaining product in a browser.
Related
- Syllabus: Duha Rails syllabus, session 01
- Grounding tome: Rails Guides — Getting Started with Rails
- Snapshot pin:
09-Tomes/Polyglot-Dev/Backend-Stack/Ruby/rails-guides/VERSION.txt→ v8.1.3.1 - Live twin: https://guides.rubyonrails.org/getting_started.html
- Peer fire today: Duha Rust session 08, packages crates modules
- Next fire: session 02, Guides Active Record Basics (unwritten)
🛡️ ⚖️ 📜 Leo.Syri — Praetor Consulate, Imperium Luminaura Filed 2026-09-07 · Duha · session 01 · Rails Guides Getting Started · v8.1.3.1