seams 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '080facbda9aa03ebeec4885f3745dd0c100d698465dcfcd7bbf35c62dd634eff'
4
- data.tar.gz: d9a6b438e0ff12c37ba4006e680a7d01f30358c1bb351cdf2e47fee9110f13be
3
+ metadata.gz: 3ef90aa737b9e8b791771fc08707df790b8a9b870b448cedc4a52824282881c9
4
+ data.tar.gz: caf6906f698570313cb0b22b90fa93cd917ea11de1e1ff05660a330149997e4d
5
5
  SHA512:
6
- metadata.gz: 1e607f46a0b749397ad5b7f7a8a1927dcd267ca14dbf77107c938269bd17c17d426ac38b98f88ba734c7d7a27adceb150973fa331e2d1b302515102b96afdccd
7
- data.tar.gz: b708e636f64a8502215b811102640df371ed57846ecd54a44dbd8ea0391f84848f4f42350b3111ddab113eb96a19a75d529f19c45b8a0726b7e6e0cd4c62b9cd
6
+ metadata.gz: b0193a281248bec66c49e53f3e34a9b278854f056cb63ad4174520b7973a826d3468e8317881b53eba28733f0090f33904372996d2adcb3e526c226d5cadc8a5
7
+ data.tar.gz: bc365fb8bffac2bb41cbc295fd8ebece8b10d998cef1135957eaba27a846bbcbfd138236c05297f6e308eeb359ad0b4ba323ad05b6fb7609315534b7c088cee7
data/CHANGELOG.md CHANGED
@@ -7,6 +7,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.1] — 2026-09-28
11
+
12
+ ### Added
13
+
14
+ - Docs: [Setting up the admin area](doc/how-to/SETTING_UP_ADMIN.md), a
15
+ step-by-step guide covering installation, the staff flag, tenant mode,
16
+ `before_admin_action`, ejecting, and adding your own dashboards. Every
17
+ step was checked in a generated app.
18
+ - Docs: the Deploying guide covers Rails 8's four production databases
19
+ (`CACHE_/QUEUE_/CABLE_DATABASE_URL`) and the Active Record encryption
20
+ keys the auth engine needs. The generated Kamal `deploy.yml` lists the
21
+ three extra database URLs.
22
+
23
+ ### Fixed
24
+
25
+ - Admin engine: an app without every canonical engine (for example only
26
+ `core` and `auth`) got a 500 on every admin page, because the
27
+ navigation loaded dashboards whose models don't exist. Those
28
+ dashboards are now hidden and their URLs return 404.
29
+ - Design generator: the next-steps message now marks
30
+ `bin/rails tailwindcss:build` as required and names the error you get
31
+ without it.
32
+ - Admin README: the "add your own dashboard" recipe now includes
33
+ `self.model`, the policies, the leading-slash route, and deleting
34
+ Administrate's generated host controller. It no longer claims
35
+ `theme_css_path` restyles the admin, since that setting is not
36
+ applied yet.
37
+ - Docs: the onboarding path now works when followed literally. Both
38
+ tutorials and the README quick start were re-run in fresh apps from
39
+ the published gem. The 10-minute tutorial claimed SQLite works (the
40
+ migrations use `jsonb` and fail), sent readers to a nonexistent
41
+ `/auth/sign_up`, and had them add mount lines the generators already
42
+ write. All onboarding docs now cover PostgreSQL, `bundle install`
43
+ after each generator, `bin/rails db:encryption:init` (without it,
44
+ sign-up raises `Missing Active Record encryption credential`), and
45
+ `bin/rails tailwindcss:build` after `design --shell`. The Engine
46
+ Catalogue's wiring example now matches what the generators write.
47
+ Docs links point at davidslv.uk/seams.
48
+
10
49
  ## [0.2.0] — 2026-09-27
11
50
 
12
51
  > [!WARNING]
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![Gem Version](https://img.shields.io/gem/v/seams.svg)](https://rubygems.org/gems/seams)
4
4
  [![CI](https://github.com/Davidslv/seams/actions/workflows/ci.yml/badge.svg)](https://github.com/Davidslv/seams/actions/workflows/ci.yml)
5
- [![Docs site](https://img.shields.io/badge/docs-davidslv.github.io%2Fseams-blue.svg)](https://davidslv.github.io/seams/)
5
+ [![Docs site](https://img.shields.io/badge/docs-davidslv.uk%2Fseams-blue.svg)](https://davidslv.uk/seams/)
6
6
  [![API docs](https://img.shields.io/badge/api-rubydoc.info-blue.svg)](https://rubydoc.info/gems/seams)
7
7
  [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
8
8
 
@@ -13,7 +13,7 @@ You ship one Rails app. Inside it, each feature (auth, accounts, billing, teams,
13
13
  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.
14
14
 
15
15
  > [!NOTE]
16
- > 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.
16
+ > 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.
17
17
 
18
18
  ## Requirements
19
19
 
@@ -28,43 +28,55 @@ Every generated file is plain Rails code in your repo. You can read it, change i
28
28
 
29
29
  ## Installation
30
30
 
31
+ Add Seams to your Gemfile:
32
+
33
+ ```ruby
34
+ # Gemfile
35
+ gem "seams", "~> 0.2"
36
+ ```
37
+
31
38
  > [!WARNING]
32
- > **Install from GitHub for now.** The only release on RubyGems is `0.1.0` (May 2026). It requires Ruby 4.0 or newer and predates many fixes, including the admin engine working at all and Ruby 3.3 support. Until a new version is released, point your Gemfile at the repository:
33
- >
34
- > ```ruby
35
- > # Gemfile
36
- > gem "seams", github: "Davidslv/seams"
37
- > ```
38
- >
39
- > Once a newer version is on RubyGems, use `gem "seams"` instead.
39
+ > **Use 0.2.0 or newer.** Version `0.1.0` requires Ruby 4.0 and predates many fixes, including the admin engine working and Ruby 3.3 support.
40
40
 
41
41
  Then install the framework:
42
42
 
43
43
  ```bash
44
44
  bundle install
45
45
  bin/rails generate seams:install
46
+ bundle install
46
47
  ```
47
48
 
48
- `seams:install` adds the framework files, a CI workflow, and a `bin/seams` command. Every step after this uses `bin/seams`.
49
+ `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`.
49
50
 
50
51
  ## Quick start
51
52
 
52
- Generate the engines you need. The order matters, because later engines build on earlier ones:
53
+ 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:
53
54
 
54
55
  ```bash
55
- bin/seams core # shared building blocks (always first)
56
- bin/seams auth # sign-in, sessions, OAuth, API tokens
57
- bin/seams accounts # the tenant (Account) and its members
58
- bin/seams notifications # in-app, email, and SMS notifications
59
- bin/seams billing # Stripe subscriptions
60
- bin/seams teams # optional teams inside an account
61
- bin/seams design --shell # UI components and an app layout
56
+ bin/seams core && bundle install # shared building blocks (always first)
57
+ bin/seams auth && bundle install # sign-in, sessions, OAuth, API tokens
58
+ bin/seams accounts && bundle install # the tenant (Account) and its members
59
+ bin/seams notifications && bundle install # in-app, email, and SMS notifications
60
+ bin/seams billing && bundle install # Stripe subscriptions
61
+ bin/seams teams && bundle install # optional teams inside an account
62
+ bin/seams design --shell && bundle install # UI components and an app layout
63
+ ```
62
64
 
63
- bundle install
65
+ Then finish the setup:
66
+
67
+ ```bash
68
+ bin/rails tailwindcss:build # the design layout loads the compiled CSS
69
+ bin/rails db:encryption:init # auth encrypts personal data; paste the printed
70
+ bin/rails credentials:edit # active_record_encryption block into credentials
64
71
  bin/rails db:migrate
65
- bin/seams list # show engines, their events, and subscribers
72
+ bin/seams list # show engines, their events, and subscribers
66
73
  ```
67
74
 
75
+ > [!IMPORTANT]
76
+ > 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`.
77
+
78
+ Start the app with `bin/rails server`. Sign up at `/auth/registration/new` and sign in at `/auth/session/new`.
79
+
68
80
  > [!TIP]
69
81
  > New to Seams? Follow **[Getting Started](doc/tutorials/GETTING_STARTED.md)**. It goes step by step from `bundle install` to a running app.
70
82
 
@@ -150,7 +162,7 @@ With these set:
150
162
  - Requests for another account's records return 404.
151
163
  - A `member` gets a 403.
152
164
 
153
- The generated `engines/admin/README.md` has the full reference.
165
+ Step-by-step guide: [Setting up the admin area](doc/how-to/SETTING_UP_ADMIN.md). The generated `engines/admin/README.md` has the full reference.
154
166
 
155
167
  </details>
156
168
 
@@ -168,7 +180,7 @@ Seams writes code into your app. So updating the gem does not change engines you
168
180
 
169
181
  ## Documentation
170
182
 
171
- 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)**.
183
+ 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)**.
172
184
 
173
185
  <details>
174
186
  <summary><strong>Start here</strong></summary>
@@ -186,6 +198,7 @@ The **[documentation site](https://davidslv.github.io/seams/)** has every guide
186
198
  - [Removing an engine](doc/how-to/REMOVING_AN_ENGINE.md)
187
199
  - [Writing an adapter](doc/how-to/WRITING_AN_ADAPTER.md): swap in Mailgun, Twilio, Paddle, and others
188
200
  - [Writing follow-up generators](doc/how-to/WRITING_FOLLOW_UP_GENERATORS.md)
201
+ - [Setting up the admin area](doc/how-to/SETTING_UP_ADMIN.md)
189
202
  - [Insertion points](doc/reference/INSERTION_POINTS.md) and the [catalogue of markers](doc/reference/INSERTION_POINTS_CATALOGUE.md)
190
203
  - [Deploying](doc/how-to/DEPLOYING.md)
191
204
 
@@ -136,9 +136,8 @@ Seams::Admin.configure do |c|
136
136
  # at request time. See "Two-mode operation" above.
137
137
  c.tenancy_scope = :platform
138
138
 
139
- # 3. theme_css_path — Optional path to a host-supplied CSS file loaded
140
- # on top of Administrate's stock styling. nil = use Administrate's
141
- # defaults.
139
+ # 3. theme_css_path — Reserved for a host-supplied CSS file on top of
140
+ # Administrate's stock styling. Accepted but not applied yet.
142
141
  c.theme_css_path = nil
143
142
 
144
143
  # 4. before_admin_action — Optional callable that runs before every
@@ -192,18 +191,51 @@ bin/seams resolve --eject admin/app/dashboards/admin/identity_dashboard.rb
192
191
  # Now edit engines/admin/app/dashboards/admin/identity_dashboard.rb freely.
193
192
  ```
194
193
 
195
- For new dashboards covering host-specific models, generate via
196
- Administrate, then move the result under the `Admin::*` namespace:
197
-
198
- ```bash
199
- bin/rails generate administrate:dashboard MyHost::Project
200
- # Move app/dashboards/my_host/project_dashboard.rb to
201
- # engines/admin/app/dashboards/admin/project_dashboard.rb
202
- # Move the controller similarly under app/controllers/admin/.
203
- # Add `resources :projects, controller: "/admin/projects"` to
204
- # engines/admin/config/routes.rb (or splice via the
205
- # admin.routes.after_resources insertion point in a follow-up generator).
206
- ```
194
+ To add a dashboard for one of your own models, e.g. `Project`:
195
+
196
+ 1. **Dashboard:** `engines/admin/app/dashboards/admin/project_dashboard.rb`.
197
+ Generate a starting point with `bin/rails generate administrate:dashboard Project`,
198
+ then move it here, rename it to `Admin::ProjectDashboard`, and declare
199
+ its model. Administrate would otherwise guess `Admin::Project`:
200
+
201
+ ```ruby
202
+ module Admin
203
+ class ProjectDashboard < Administrate::BaseDashboard
204
+ def self.model
205
+ ::Project
206
+ end
207
+ # ATTRIBUTE_TYPES, COLLECTION_ATTRIBUTES, ... as generated
208
+ end
209
+ end
210
+ ```
211
+
212
+ 2. **Controller:** `engines/admin/app/controllers/admin/projects_controller.rb`,
213
+ subclassing the gated base controller. Delete the controller
214
+ Administrate generated in your app's `app/controllers/admin/`, because
215
+ it bypasses the gate:
216
+
217
+ ```ruby
218
+ module Admin
219
+ class ProjectsController < ::Seams::Admin::ApplicationController
220
+ def resource_class = ::Project
221
+ def dashboard = (@dashboard ||= ProjectDashboard.new)
222
+ end
223
+ end
224
+ ```
225
+
226
+ 3. **Policies:** `Admin::Platform::ProjectPolicy` and `Admin::Tenant::ProjectPolicy`
227
+ under `engines/admin/app/policies/admin/{platform,tenant}/`. Subclass the
228
+ namespace's `ApplicationPolicy`. The controller name picks the policy, and
229
+ an action with no policy is denied.
230
+
231
+ 4. **Route:** add this to `engines/admin/config/routes.rb`, inside the
232
+ `scope as: :admin do` block:
233
+
234
+ ```ruby
235
+ resources :projects, controller: "/admin/projects"
236
+ ```
237
+
238
+ The leading slash is required. The sidebar picks the dashboard up automatically.
207
239
 
208
240
  Administrate's full dashboard / field / controller surface is documented
209
241
  at <https://administrate-demo.herokuapp.com/>.
@@ -222,8 +254,9 @@ at <https://administrate-demo.herokuapp.com/>.
222
254
 
223
255
  - **2FA / IP allow-list for `/admin`.** Not shipped. Wire your own via
224
256
  `Seams::Admin.config.before_admin_action`.
225
- - **Branded UI.** Stock Administrate CSS only in v1. Override via
226
- `theme_css_path`.
257
+ - **Branded UI.** Stock Administrate CSS only. `theme_css_path` is
258
+ accepted but not applied yet. To restyle, override Administrate's
259
+ views (see Administrate's documentation).
227
260
  - **Search beyond Administrate's stock.** Administrate ships a basic
228
261
  Ransack-backed search; rich filtering / faceted search is your job.
229
262
  - **Batch actions.** Not in Administrate's surface; not shipped here.
@@ -73,11 +73,21 @@ module Seams
73
73
  # declared model so follow-up dashboards are picked up too.
74
74
  def self.admin_route_keys_by_model
75
75
  admin_routes.map(&:first).uniq.each_with_object({}) do |key, map|
76
- dashboard = "Admin::#{key.classify}Dashboard".safe_constantize
77
- map[dashboard.model.name] = key if dashboard.respond_to?(:model)
76
+ model = admin_model_for(key)
77
+ map[model.name] = key if model
78
78
  end
79
79
  end
80
80
 
81
+ # The model behind a dashboard route key, or nil when its engine is
82
+ # not installed (a host with auth but no billing has no
83
+ # Billing::Plan). Those dashboards are hidden and answer 404.
84
+ def self.admin_model_for(key)
85
+ dashboard = "Admin::#{key.classify}Dashboard".safe_constantize
86
+ dashboard.model if dashboard.respond_to?(:model)
87
+ rescue NameError
88
+ nil
89
+ end
90
+
81
91
  # Accounts::Membership and Teams::Membership both have the route
82
92
  # key `memberships` (isolated-engine model naming), so
83
93
  # Administrate's polymorphic `[:admin, membership]` calls
@@ -153,6 +163,10 @@ module Seams
153
163
  helper_method :seams_admin_tenancy_scope
154
164
  helper_method :seams_admin_navigation
155
165
 
166
+ # Runs after the authentication gate, so signed-out visitors are
167
+ # still redirected to sign-in rather than told what exists.
168
+ before_action :ensure_admin_model_installed
169
+
156
170
  def seams_admin_tenancy_scope
157
171
  ::Seams::Admin.config.tenancy_scope
158
172
  end
@@ -219,6 +233,12 @@ module Seams
219
233
  t("administrate.controller.#{key}", resource: resource_class.model_name.human)
220
234
  end
221
235
 
236
+ def ensure_admin_model_installed
237
+ return if self.class.admin_model_for(controller_name)
238
+
239
+ raise ActionController::RoutingError, "#{controller_name} is not installed in this app"
240
+ end
241
+
222
242
  def membership_route_key(record)
223
243
  MEMBERSHIP_ROUTE_KEYS[record.class.name] || controller_name.singularize
224
244
  end
@@ -270,11 +290,11 @@ module Seams
270
290
  # an index route that the current user may list.
271
291
  def seams_admin_navigation
272
292
  self.class.admin_routes.select { |_key, action| action == "index" }.map(&:first).uniq.filter_map do |key|
273
- dashboard = "Admin::#{key.classify}Dashboard".safe_constantize
274
- next unless dashboard.respond_to?(:model) && authorized_action?(dashboard.model, :index)
293
+ model = self.class.admin_model_for(key)
294
+ next unless model && authorized_action?(model, :index)
275
295
 
276
296
  { key: key,
277
- label: dashboard.model.model_name.human(count: 2),
297
+ label: model.model_name.human(count: 2),
278
298
  path: url_for(controller: "/admin/#{key}", action: :index),
279
299
  active: key == controller_name }
280
300
  end
@@ -620,8 +620,12 @@ module Seams
620
620
  1. bundle install
621
621
  (picks up tailwindcss-rails, injected into the host Gemfile)
622
622
 
623
- 2. bin/rails tailwindcss:install (if Tailwind isn't set up yet)
624
- then build it: bin/rails tailwindcss:build
623
+ 2. bin/rails tailwindcss:build (required)
624
+ Pages using the layout fail with "The asset 'tailwind.css' was
625
+ not found" until the CSS is built. Rebuild after style changes,
626
+ or keep `bin/rails tailwindcss:watch` running.
627
+ Without --shell, if your layout doesn't load Tailwind yet, run
628
+ `bin/rails tailwindcss:install` first.
625
629
 
626
630
  3. Use the components anywhere in the host or another engine's views:
627
631
  <%= ui_button(variant: :primary) { "Save" } %>
@@ -37,6 +37,12 @@ env:
37
37
  secret:
38
38
  - RAILS_MASTER_KEY
39
39
  - DATABASE_URL
40
+ # Rails 8 has separate cache/queue/cable databases; DATABASE_URL only
41
+ # covers the primary. Point these at the same server with different
42
+ # database names, or configure them in config/database.yml and drop them.
43
+ - CACHE_DATABASE_URL
44
+ - QUEUE_DATABASE_URL
45
+ - CABLE_DATABASE_URL
40
46
  - REDIS_URL
41
47
  - STRIPE_SECRET_KEY
42
48
  - STRIPE_WEBHOOK_SECRET
data/lib/seams/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Seams
4
- VERSION = "0.2.0"
4
+ VERSION = "0.2.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: seams
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Silva