Lesson 1 — Rails 8 with PostGIS
In this lesson you’ll get your machine ready and clone the dispatch deck starter. By the end, you’ll have a running Rails 8 application with PostGIS installed, three seeded user accounts, and the chassis we’ll fill in over the rest of the tutorial. We’ll walk through what’s in the starter so you have a feel for the structure before we start writing spatial code in Lesson 3.
Two things to install
The application needs two things on your machine:
- PostgreSQL 14 or later — the database. Most of the tutorial will run on PostgreSQL 16, which is what we test against.
- PostGIS 3.3 or later — the spatial extension that adds geometry types, spatial functions, and spatial indexes to PostgreSQL.
PostGIS is an extension, not a separate program. You install PostgreSQL the normal way, then install the PostGIS extension package, and then enable it inside any database that needs it. Different databases on the same PostgreSQL server can have or not have PostGIS as you choose.
We won’t use Docker. The tutorial assumes a native install because it’s easier for development — no port-mapping headaches, faster startup, and the install instructions are the same as what you’d write up for a colleague.
Installing on macOS
The shortest path on macOS is Homebrew:
|
|
Homebrew links PostGIS against the most recently installed PostgreSQL formula. If you already have an older PostgreSQL installation that’s running, stop it first or you’ll have two servers competing for port 5432.
Installing on Linux
On Debian or Ubuntu, the official PostgreSQL APT repository has
the most current versions. The bundled postgresql-16-postgis-3
package installs both pieces:
|
|
Other distributions have similar packages: postgresql-postgis
on Fedora, postgis on Arch. The PostGIS website at postgis.net
has a per-distribution installation guide if you need it.
Installing on Windows
Use WSL2 with Ubuntu and follow the Linux instructions above. The native Windows PostgreSQL installer ships with PostGIS as a Stack Builder add-on, but the rest of the tutorial assumes a Unix-style command line, so WSL is the path of least friction.
Activity 1 — Verify Postgres is running
In a terminal, run:
|
|
You should see something like psql (PostgreSQL) 16.4. If the
command isn’t found, your install hasn’t put psql on your PATH;
fix that before continuing. The most common cause on macOS is
that Homebrew installed it but didn’t add it to PATH — brew link --force postgresql@16 fixes it.
Then check that the server is running:
|
|
You should see the PostgreSQL version banner. If you get
could not connect to server, the server isn’t running. Start it
(brew services start postgresql@16 on macOS,
sudo systemctl start postgresql on Linux) and try again.
Verify PostGIS is available
Installing PostGIS makes the extension available. It doesn’t
enable it in any specific database; you do that per-database
with CREATE EXTENSION postgis. We’ll do that in a moment, but
first let’s confirm PostGIS is actually present on the server.
Activity 2 — Verify PostGIS is installed
In psql, connect to the default postgres database and check
the available extensions:
|
|
You should see something like:
|
|
If postgis isn’t in that list, the extension package isn’t
installed. Go back and install it before continuing.
Type \q to exit psql.
If PostGIS is in the list, you’re ready. We’ll enable it in the dispatch deck’s database in a moment.
Cloning the starter
The dispatch deck chassis is what you start the tutorial with — a working Rails 8 application with the layout, authentication, seed users, and visual styling already in place. You don’t build the chassis; you build the spatial work that goes inside it.
The starter is available two ways:
As a Git tag on the tutorial repository. This is the canonical way and what we recommend if you have Git installed and an SSH key configured for GitHub:
|
|
As a zip download. If you’d rather not use Git, the starter
is also available as a zip from the tutorial site. Download it,
unzip, and cd into the resulting directory.
Either way, you should now have a directory called
vera_dispatch_manager with the Rails app inside it.
Make sure your database.yml uses the postgis adapter
For PostGIS to be available to ActiveRecord, config/database.yml
must set adapter: postgis (not the default adapter: postgresql).
The dispatch deck chassis ships with this set correctly, but if you
ever generate a new Rails app from scratch, you’ll need to change it
yourself — the rails new generator defaults to plain PostgreSQL.
If migrations fail with undefined method 'st_point' or similar,
the adapter isn’t engaged. Check database.yml, fix the line, then
bin/rails db:drop db:create db:migrate to recreate the database
with the right adapter active.
Activity 3 — Run bin/setup
The starter ships with a bin/setup script that handles the
conventional Rails setup steps: bundle install, database create,
database migrate, database seed.
|
|
This may take a minute the first time, mostly because it’s
installing Ruby gems. While it runs, the script prints what it’s
doing — you’ll see bundle install, then rails db:create, then
rails db:migrate, then rails db:seed.
If bin/setup fails on the database step, the most likely cause
is that PostgreSQL isn’t running, or that the dispatch deck’s
configuration is using a Postgres user that doesn’t exist on
your machine. Check config/database.yml and adjust the username
to match your local Postgres setup.
When bin/setup finishes successfully, you should see a message
indicating that the database has been seeded with three users.
Now let’s start the development server.
Running the application
|
|
This is Rails 8’s standard development command. It launches three processes simultaneously: the Rails web server (Puma), the Tailwind CSS watcher that recompiles your styles when files change, and the Solid Queue worker for background jobs. They all run in the same terminal with their output multiplexed.
Open a browser to http://localhost:3000 and you should land on the sign-in page. The brand wordmark sits above a centred card with email and password fields. Below the card, in a development- only “Development sign-in” section, you’ll see three buttons — one for each seeded user.
Activity 4 — Sign in as each role
Click “Sign in as Manager”. You should land on the dashboard page, which currently shows four metric cards (all empty) and two larger placeholder panels.
Notice the sidebar on the left, dark slate with five items: Dashboard, Jobs, Technicians, Service Areas, Reports.
Now find the “DEV” dropdown in the top right of the page (next to your name and the Sign out button). Click it and choose “Dispatcher”. Watch what happens.
You should be redirected — the Dispatcher’s default landing page is the Jobs index, not the Dashboard. The sidebar also changes: Reports has disappeared because dispatchers don’t see reports. The role badge in the top bar now reads “Dispatcher” instead of “Manager”.
Switch one more time to “Field Officer”. This time:
- The default landing is still Jobs (we’ll customise the field officer’s view in Module 4 to be “my jobs only”)
- Service Areas and Technicians are gone from the sidebar — field officers don’t manage these
- The role badge updates accordingly
This is the role-aware UI we discussed in Module 1, working live. Spend a minute clicking around and getting a feel for what each role can and cannot see.
What’s in the starter
Now that the application is running, let’s walk through what’s actually in the codebase. We don’t need to understand every line — most of what’s here is conventional Rails — but a few specific pieces will matter throughout the tutorial.
The directory structure follows a standard Rails layout:
|
|
The most important pieces for this tutorial are:
app/components/ — about a dozen Phlex components making up
the chassis. The top bar, sidebar, role switcher, role badge,
data table, page header, auth card, and so on. We won’t change
these often; they’re the visual chrome around the spatial work
we’ll add.
app/views/layouts/application.html.erb — the page-level
shell. This is what wraps every page with the top bar, sidebar,
and chrome. Configures Turbo morph as the default refresh
strategy. Loads the MapLibre stylesheet that maps need.
config/database.yml — the database adapter is set to
postgis (rather than postgresql). This is what enables
ActiveRecord to work with geometry columns transparently. Open
it now and you’ll see lines like:
|
|
The postgis adapter is provided by the
activerecord-postgis-adapter gem and is a drop-in replacement
for postgresql. It supports all standard PostgreSQL features
plus the spatial types.
config/importmap.rb — the JavaScript dependency manifest.
Already pinned to MapLibre, Geoman (for drawing), and the Vera
gem’s controllers. We’ll talk about Vera’s contribution
specifically in Lesson 2.
db/seeds.rb — creates three users:
manager@vera.test, dispatcher@vera.test, and
field_officer@vera.test. All three share the password
password for development convenience. The dev role switcher
signs you in as the appropriate seed user.
Verifying PostGIS is enabled in the dispatch database
Earlier we verified PostGIS is available on the server. Let’s
confirm it’s actually enabled in the dispatch deck’s database —
which bin/setup should have done as part of running migrations.
Activity 5 — Verify PostGIS is enabled
In a terminal:
|
|
This opens a psql session connected to the development database.
Now check:
|
|
You should see something like:
|
|
That confirms PostGIS is enabled and ready in this specific
database. If you get function postgis_version() does not exist,
PostGIS isn’t enabled here — most likely your database.yml
isn’t using the postgis adapter, or the database was created
before the adapter was switched on. Drop and recreate with
bin/rails db:drop db:create db:migrate db:seed and try again.
Type \q to exit dbconsole.
The reason PostGIS shows up automatically in this database is that
the activerecord-postgis-adapter gem hooks into Rails’s database
tasks: when you run db:create, the adapter installs the PostGIS
extension as part of creating the database. You don’t need an
explicit enable_extension "postgis" migration to get PostGIS
working — the adapter handles it. (You’d usually add an explicit
migration anyway, for schema.rb transparency. We’ll discuss this
in Module 3 when we add our first geometry column.)
Where this leaves us
You now have:
- PostgreSQL 16 (or your version) installed and running
- PostGIS 3 installed and enabled in the dispatch deck’s development database
- The Rails 8 dispatch deck application cloned and running locally
- Three seed users: manager, dispatcher, field officer
- The dev role switcher working — you can switch between roles and see different views
- A clear sense of what’s in the codebase
What you don’t have yet, and what comes next:
- Any maps. The chassis is map-ready (the MapLibre stylesheet is loaded, the Vera importmap pins are configured) but no page actually renders one yet.
- Any spatial data. There’s a
userstable from the authentication generator, but noJob,Technician,Depot, orServiceAreamodels. We’ll add these progressively. - Any meaningful interaction beyond signing in and switching roles.
Lesson 2 explains what the Vera gem brings to the chassis — what it does, what it doesn’t do, the API surface, and the relationship to MapLibre. Lesson 3 puts the first map on the screen.
If something on this lesson didn’t work — install errors, the app didn’t start, a seed user didn’t sign in correctly — fix it now before continuing. The rest of the tutorial assumes a working chapter-2-1-end state, and tracking down setup issues mid-chapter is harder than fixing them now.