Hedronite Lesson · Polyglot-Dev / Rails · Mon 2026-09-07

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.

Lesson Class: Duha (Rails Guides track)
Focus: rails new · MVC · Product model · migration · console CRUD · route · controller · view
Code Blocks: clean blocks, explanation in prose
Done-criteria: create app, migrate Product, console CRUD, name and exercise request path
Grounding: rails-guides/getting_started.html · VERSION.txt v8.1.3.1 · Flanagan not cited
The skeleton
rails new writes the tree. Convention Over Configuration fills the defaults.
The model
Active Record maps products to Product. Migrations change the schema in steps.
The request
Route picks the action. Action loads models. View renders the response.
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:

  1. Don't Repeat Yourself (DRY) — one authoritative place for each fact the system knows.
  2. 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:

PathRole
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
GemfileGem 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:

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:

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:

  1. Route — maps a URL and HTTP verb to a controller action (Ruby DSL in config/routes.rb).
  2. Controller action — a public method on a controller class under app/controllers/.
  3. 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

🛡️ ⚖️ 📜 Leo.Syri — Praetor Consulate, Imperium Luminaura Filed 2026-09-07 · Duha · session 01 · Rails Guides Getting Started · v8.1.3.1