Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Fixed

- Docs: the onboarding path now works when followed literally. Both
tutorials and the README quick start were re-run in fresh apps from
the published gem. The 10-minute tutorial claimed SQLite works (the
migrations use `jsonb` and fail), sent readers to a nonexistent
`/auth/sign_up`, and had them add mount lines the generators already
write. All onboarding docs now cover PostgreSQL, `bundle install`
after each generator, `bin/rails db:encryption:init` (without it,
sign-up raises `Missing Active Record encryption credential`), and
`bin/rails tailwindcss:build` after `design --shell`. The Engine
Catalogue's wiring example now matches what the generators write.
Docs links point at davidslv.uk/seams.

## [0.2.0] — 2026-09-27

> [!WARNING]
Expand Down
40 changes: 26 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[![Gem Version](https://img.shields.io/gem/v/seams.svg)](https://rubygems.org/gems/seams)
[![CI](https://github.com/Davidslv/seams/actions/workflows/ci.yml/badge.svg)](https://github.com/Davidslv/seams/actions/workflows/ci.yml)
[![Docs site](https://img.shields.io/badge/docs-davidslv.github.io%2Fseams-blue.svg)](https://davidslv.github.io/seams/)
[![Docs site](https://img.shields.io/badge/docs-davidslv.uk%2Fseams-blue.svg)](https://davidslv.uk/seams/)
[![API docs](https://img.shields.io/badge/api-rubydoc.info-blue.svg)](https://rubydoc.info/gems/seams)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)

Expand All @@ -13,7 +13,7 @@
Every generated file is plain Rails code in your repo. You can read it, change it, or delete it. Nothing is hidden behind the gem.

> [!NOTE]
> Seams is the executable companion to the book **[Modular Rails: Architecture for the Long Game](https://davidslv.uk/modular-rails/)**. The full guides live on the **[documentation site](https://davidslv.github.io/seams/)**. [seams-example](https://github.com/Davidslv/seams-example) is a reference host with every engine wired up.
> Seams is the executable companion to the book **[Modular Rails: Architecture for the Long Game](https://davidslv.uk/modular-rails/)**. The full guides live on the **[documentation site](https://davidslv.uk/seams/)**. [seams-example](https://github.com/Davidslv/seams-example) is a reference host with every engine wired up.

Check warning on line 16 in README.md

View workflow job for this annotation

GitHub Actions / Prose lint (advisory)

[vale] reported by reviewdog 🐶 'Architecture' should be in lowercase. Raw Output: {"message":"'Architecture' should be in lowercase.","location":{"path":"README.md","range":{"start":{"line":16,"column":67},"end":{"line":16,"column":79}}},"severity":"WARNING","code":{"value":"Google.Colons","url":"https://developers.google.com/style/colons"}}

## Requirements

Expand Down Expand Up @@ -43,28 +43,40 @@
```bash
bundle install
bin/rails generate seams:install
bundle install
```

`seams:install` adds the framework files, a CI workflow, and a `bin/seams` command. Every step after this uses `bin/seams`.
`seams:install` adds the framework files, a CI workflow, a `bin/seams` command, and a few development gems (hence the second `bundle install`). Every step after this uses `bin/seams`.

## Quick start

Generate the engines you need. The order matters, because later engines build on earlier ones:
Generate the engines you need. The order matters, because later engines build on earlier ones. Each generator can add gems to your Gemfile, so run `bundle install` after each one, before the next:

```bash
bin/seams core # shared building blocks (always first)
bin/seams auth # sign-in, sessions, OAuth, API tokens
bin/seams accounts # the tenant (Account) and its members
bin/seams notifications # in-app, email, and SMS notifications
bin/seams billing # Stripe subscriptions
bin/seams teams # optional teams inside an account
bin/seams design --shell # UI components and an app layout
bin/seams core && bundle install # shared building blocks (always first)
bin/seams auth && bundle install # sign-in, sessions, OAuth, API tokens
bin/seams accounts && bundle install # the tenant (Account) and its members
bin/seams notifications && bundle install # in-app, email, and SMS notifications
bin/seams billing && bundle install # Stripe subscriptions
bin/seams teams && bundle install # optional teams inside an account
bin/seams design --shell && bundle install # UI components and an app layout
```

bundle install
Then finish the setup:

```bash
bin/rails tailwindcss:build # the design layout loads the compiled CSS
bin/rails db:encryption:init # auth encrypts personal data; paste the printed
bin/rails credentials:edit # active_record_encryption block into credentials
bin/rails db:migrate
bin/seams list # show engines, their events, and subscribers
bin/seams list # show engines, their events, and subscribers
```

> [!IMPORTANT]
> Skip the encryption keys and sign-up fails with `Missing Active Record encryption credential`. Skip the Tailwind build and every page using the design layout fails with `The asset 'tailwind.css' was not found`.

Start the app with `bin/rails server`. Sign up at `/auth/registration/new` and sign in at `/auth/session/new`.

> [!TIP]
> New to Seams? Follow **[Getting Started](doc/tutorials/GETTING_STARTED.md)**. It goes step by step from `bundle install` to a running app.

Expand Down Expand Up @@ -168,7 +180,7 @@

## Documentation

The **[documentation site](https://davidslv.github.io/seams/)** has every guide below, with search. The API reference is on **[rubydoc.info](https://rubydoc.info/gems/seams)**.
The **[documentation site](https://davidslv.uk/seams/)** has every guide below, with search. The API reference is on **[rubydoc.info](https://rubydoc.info/gems/seams)**.

<details>
<summary><strong>Start here</strong></summary>
Expand Down
35 changes: 23 additions & 12 deletions doc/reference/ENGINE_CATALOGUE.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,28 +248,39 @@ the example `_quire.css` theme. See

## Pulling them together

A real B2B SaaS using all six:
A B2B SaaS using the canonical engines. The generators write the
routes for you. After `core`, `auth`, `accounts`, `notifications`,
`billing`, `teams` and `admin`, `config/routes.rb` contains:

```ruby
# config/routes.rb
mount Core::Engine, at: "/" # no public routes — mount for completeness
mount Auth::Engine, at: "/auth"
mount Accounts::Engine, at: "/accounts"
mount Notifications::Engine, at: "/notifications"
mount Billing::Engine, at: "/billing"
# config/routes.rb (written by the generators)
mount Seams::Admin::Engine, at: "/admin"
mount Teams::Engine, at: "/teams"
mount Billing::Engine, at: "/billing"
mount Notifications::Engine, at: "/notifications"
mount Accounts::Engine, at: "/accounts"
mount Auth::Engine, at: "/auth"
mount Core::Engine, at: "/core"
```

The auth generator also adds `include Auth::Authentication` to your
`ApplicationController`. Add the other two concerns yourself, so the
per-request `Current` attributes cascade into core and accounts:

```ruby
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
include Auth::Authentication # 1. sets Auth::Current.identity
include Core::HasCurrentAttributes # 2. reads Auth::Current.identity (depends on 1)
include Accounts::Authorization # 3. reads Auth::Current.identity (depends on 1)
before_action :authenticate_identity!
include Auth::Authentication # 1. added by `bin/seams auth`; sets Auth::Current.identity
include Core::HasCurrentAttributes # 2. add this; reads Auth::Current.identity (depends on 1)
include Accounts::Authorization # 3. add this; reads Auth::Current.identity (depends on 1)
before_action :authenticate_identity! # optional: require sign-in app-wide
end
```

With the app-wide `before_action`, host pages that must stay public
need `skip_before_action :authenticate_identity!`. The auth engine's
own sign-in, sign-up and password-reset actions already skip it.

Order matters: `Auth::Authentication` must be included BEFORE
`Core::HasCurrentAttributes` and `Accounts::Authorization` so
`Auth::Current.identity` is bound before the other engines read it.
Expand All @@ -290,5 +301,5 @@ class User < ApplicationRecord
end
```

That's a Bullet-Train-class app surface in six `bin/seams`
That's a Bullet-Train-class app surface in a handful of `bin/seams`
commands. The code is yours; nothing is hidden behind a gem.
84 changes: 47 additions & 37 deletions doc/tutorials/GETTING_STARTED.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,21 @@

- Ruby 3.3+
- Rails 7.1+ (8.x recommended)
- PostgreSQL. The engines use `jsonb` columns, so SQLite and MySQL are
not supported. For a new app: `rails new myapp --database=postgresql`.
- A new or existing Rails application

## 1. Install

```ruby
# Gemfile
gem "seams"
gem "seams", "~> 0.2"
```

```bash
bundle install
bin/rails generate seams:install
bundle install # install adds gems (rspec-rails, rubocop, brakeman, ...)
```

The install generator scaffolds:
Expand All @@ -32,56 +35,57 @@
- `script/run_affected_tests.sh`, `script/collate_coverage.rb` — host-local helpers
- `doc/ARCHITECTURE.md` — per-host architecture template

## 2. Generate your first engine
## 2. Generate your first engines

`core` comes first. Every other engine builds on it.

```bash
bin/seams core
bin/seams auth
```

Look at what it created:
Look at what `auth` created:

```bash
$ tree engines/auth -L 2
engines/auth
├── LICENSE
├── README.md
├── auth.gemspec
├── app/
│ ├── controllers/
│ ├── models/
│ └── views/
├── config/
│ └── routes.rb
├── db/
│ └── migrate/
├── lib/
└── spec/
$ ls engines/auth
app auth.gemspec config db Gemfile lib LICENSE Rakefile README.md spec
$ ls engines/auth/app
controllers jobs mailers models services views
```

## 3. Wire it up
## 3. What the generators wire up, and what you do

Add the engine's mount line to your host routes:
The generators edit your app for you:

```ruby
# config/routes.rb
Rails.application.routes.draw do
mount Auth::Engine, at: "/auth"
end
```
- a `mount` line per engine in `config/routes.rb` (`/auth`, `/core`, ...)

Check warning on line 60 in doc/tutorials/GETTING_STARTED.md

View workflow job for this annotation

GitHub Actions / Prose lint (advisory)

[vale] reported by reviewdog 🐶 In general, don't use an ellipsis. Raw Output: {"message":"In general, don't use an ellipsis.","location":{"path":"doc/tutorials/GETTING_STARTED.md","range":{"start":{"line":60,"column":70},"end":{"line":60,"column":73}}},"severity":"WARNING","code":{"value":"Google.Ellipses","url":"https://developers.google.com/style/ellipses"}}
- `include Auth::Authentication` in your `ApplicationController`
- `config/initializers/<engine>.rb` for each engine
- any gems the engine needs, in your Gemfile

Add the authentication concern to your ApplicationController:
You do three things:

```ruby
class ApplicationController < ActionController::Base
include Auth::Authentication
end
```
1. **Install the gems they added:**

Run migrations:
```bash
bundle install
```

```bash
bin/rails db:migrate
```
2. **Create encryption keys.** The auth engine encrypts personal data
at rest with Active Record encryption. Without keys, sign-up fails
with `Missing Active Record encryption credential`.

```bash
bin/rails db:encryption:init # prints an active_record_encryption: block
bin/rails credentials:edit # paste the block in and save
```

3. **Run the migrations:**

```bash
bin/rails db:migrate
```

Sign-up is at `/auth/registration/new` and sign-in at `/auth/session/new`.

## 4. Generate more engines

Expand All @@ -102,11 +106,17 @@
`.rubocop.yml` files are auto-updated so the boundary cops cover
the new engine without manual edits.

Run `bundle install` and `bin/rails db:migrate` after generating. Several
engines add gems: billing adds `stripe`, admin adds `administrate` and

Check warning on line 110 in doc/tutorials/GETTING_STARTED.md

View workflow job for this annotation

GitHub Actions / Prose lint (advisory)

[vale] reported by reviewdog 🐶 Use 'administrator' instead of 'admin'. Raw Output: {"message":"Use 'administrator' instead of 'admin'.","location":{"path":"doc/tutorials/GETTING_STARTED.md","range":{"start":{"line":110,"column":42},"end":{"line":110,"column":47}}},"severity":"WARNING","code":{"value":"Google.WordListCase","url":"https://developers.google.com/style/word-list"}}
`pundit`, design adds `tailwindcss-rails`. After `bin/seams design`, also
run `bin/rails tailwindcss:build`, because the layout loads the compiled CSS.

The recommended order is `core → auth → accounts → notifications →
billing → teams`. Some engines depend on each other (accounts on
auth, billing on accounts) — see each engine's README for the
"Requires:" line. `permissions`, `admin`, and `design` are opt-in and
can be added at any time. Run `bin/seams design --shell` to also get a
can be added at any time. Only staff can open the admin area: set

Check warning on line 118 in doc/tutorials/GETTING_STARTED.md

View workflow job for this annotation

GitHub Actions / Prose lint (advisory)

[vale] reported by reviewdog 🐶 Use 'administrator' instead of 'admin'. Raw Output: {"message":"Use 'administrator' instead of 'admin'.","location":{"path":"doc/tutorials/GETTING_STARTED.md","range":{"start":{"line":118,"column":51},"end":{"line":118,"column":56}}},"severity":"WARNING","code":{"value":"Google.WordListCase","url":"https://developers.google.com/style/word-list"}}
`staff: true` on an `Auth::Identity` to let someone in. Run `bin/seams design --shell` to also get a
ready-to-boot application layout and signed-in dashboard, which makes
the other engines visible in a real, styled UI.

Expand Down
Loading
Loading