Generators
OpenLoam ships exactly two generators. There is no third — every entity, whether
hand-authored or agent-authored, goes through open_loam:entity; there is no
separate “quick model” path.
open_loam:install
bin/rails g open_loam:install
Installs the foundation into a host Rails app, in order
(lib/generators/open_loam/install/install_generator.rb):
- Migrations for every OpenLoam-owned table (tenants, memberships, audit records, field definitions, notifications, API tokens, webhook endpoints, comments, configs, MFA credentials, pending actions, perspectives, record locks, business rules, search tokens, SSO providers, dictionaries, progress jobs, scheduled jobs, dashboard widgets, translations, auth attempts, custom field values, event deliveries, inbound webhooks).
- ActiveStorage install (attachments need it — generated entities
include OpenLoam::Attachable; skipped with a warning if ActiveStorage isn’t available). - A minimal
Usermodel withhas_secure_password. config/initializers/open_loam.rb— see Configuration.AGENTS.mdat the app root — the contract. See The agent contract.- The full admin surface — controllers, views, and the
namespace :admin do … endroute block (sessions, MFA, sudo, pending actions, perspectives, business rules, SSO providers, dictionaries, progress jobs, scheduled jobs, event deliveries, inbound webhook sources, history/undo, imports, field definitions, notifications, webhook endpoints, API tokens, comments, configs, features, search, API docs) plus the publicPOST /webhooks/:tokeninbound receiver. app/controllers/api/base_controller.rb— the JSON API base.test/open_loam_guardrails_test.rb— the structural guardrail suite. See Guardrails.- Test-helper wiring — if
test/test_helper.rbexists, injectsrequire "open_loam/test_helpers"andinclude OpenLoam::TestHelpers. If the app was generated with--skip-test, this step is skipped with instructions to wire it manually.
Options
| Flag | Default | Effect |
|---|---|---|
--primary-key-type TYPE |
the app’s own setting, then bigint |
Key type for the generated tables: bigint, uuid, or string. |
--key-limit N |
36 for string |
Column limit for a string key. Ignored for bigint and uuid. |
Re-running is safe for template files (Thor’s default overwrite/skip prompt applies) but not idempotent for migrations — it’s a once-per-app command.
Primary keys
OpenLoam’s tables follow the host app’s key type rather than assuming bigint. The
type is taken from --primary-key-type, then from the app’s own
config.generators setting, then bigint:
# config/application.rb — OpenLoam reads this, so you don't pass a flag
config.generators do |g|
g.orm :active_record, primary_key_type: :string
end
That renders create_table :open_loam_tenants, id: :string, limit: 36, matching
type: on every t.references, and matching column types on the polymorphic
*_id columns (auditable_id, commentable_id, searchable_id, and the
rest) that can’t use t.references. A bigint app gets exactly the migrations
it always got — no id:, no type:.
A non-integer key has no database default, so OpenLoam::GeneratedKey assigns a
UUID in before_create for every OpenLoam model and for the generated User.
Integer keys take an early return and are still generated by the database.
open_loam:entity reads the same setting, so entities generated later match.
open_loam:entity
bin/rails g open_loam:entity Equipment name:string daily_rate:decimal status:string --domain rental
The interface for adding a business entity — for humans and AI agents
alike (lib/generators/open_loam/entity/entity_generator.rb). One command
produces a tenant-scoped, audited, evented model, its policy, admin + API
controllers and views, and the isolation tests that prove the guardrails
hold.
Arguments
NAME— the entity’s class name (e.g.Equipment).field:type field:type ...— standard Rails attribute syntax, same asrails g model.
Options
| Flag | Default | Effect |
|---|---|---|
--domain STRING |
"app" |
Event domain prefix — publishes as <domain>.<entity>.<happened> (e.g. --domain rental → rental.equipment.created). |
--encrypt field field |
[] |
Encrypt these fields at rest, per tenant (OpenLoam::Encryptable). |
--encrypt-searchable field field |
[] |
Encrypt and add a blind index for exact-match lookup (find_by_<field> still works). |
--primary-key-type TYPE |
the app’s own setting, then bigint |
Key type for the entity’s table — see Primary keys. |
--key-limit N |
36 for string |
Column limit for a string key. |
What it generates
db/migrate/create_<table>.rbapp/models/<name>.rbapp/policies/<name>_policy.rbapp/controllers/admin/<plural>_controller.rb+ full CRUD views (index, show, new, edit,_form, and adeletedrecycle-bin view)app/controllers/api/<plural>_controller.rbtest/entities/<name>_test.rb— the generated guardrail/isolation tests (tenant invisibility, missing-context raise, cross-tenant write rejection, audit-on-create, event-on-create, soft-delete/restore, policy denial for a non-member) — see the template atlib/generators/open_loam/entity/templates/entity_test.rb.- Routes:
resources :<plural>injected into the existingnamespace :adminblock (withdeleted/restore/export/bulkmember/collection routes) and intonamespace :api(JSON format). Re-running the generator is a no-op for routes already present.
Only string/text columns become searchable_by targets automatically
(and only if not encrypted — ciphertext isn’t LIKE-able). A generated form
field’s input helper is chosen from the column type (check_box for
boolean, date_field for date, number_field for integer/decimal/references,
etc.).