Generators

lode re-expresses Ecto’s and Phoenix’s code generators — mix ecto.gen.migration, mix phx.gen.schema, mix phx.gen.context — without macros and without a global mix. There is no archive to install: a generator is a runnable module in your project that you invoke with gleam run -m <task>, built on the pure functions in lode/gen/schema and lode/migration/gen.

The ready-made runnables live in examples/codegen_demo/src/gen_schema.gleam and gen_context.gleam. Copy them into your own project’s src/ (they’re thin wrappers over lode/gen/schema) and the commands below work as shown.

The -- separator. gleam run consumes flags like --standalone itself, so pass them after a --: gleam run -m gen_schema -- --standalone …. Positional args (records, tables, field:type) need no separator.

gen.schema — a schema + migration

The macro-less mix phx.gen.schema. It parses a <Record> <table> <field:type>… command line into either a spec entry or a finished module, plus a create_table migration.

Spec mode (default)

gleam run -m gen_schema Product products name:string price:decimal in_stock:boolean

Writes src/create_products.gleam (the migration) and prints a spec.table(...) snippet to paste into your db_spec.gleam. Then regenerate the typed schema modules:

gleam run -m regenerate

This is the §14-pure path: the spec stays the single source of truth, so the record, Schema(row), accessors, and *Fields are all generated and can’t drift.

Standalone mode

gleam run -m gen_schema -- --standalone Post posts title:string author_id:references:users

Writes a finished, hand-owned schema module src/post.gleam and the migration src/create_posts.gleam — Phoenix-style. Use this when a table needs hand-tuning the codegen can’t express; you then own that file (no spec entry, no regenerate).

Both modes refuse to overwrite an existing file, so re-running is safe.

In standalone mode a references: becomes a plain foreign-key column only — not a belongs_to — because codegen validates that an association’s table is in the batch, and a one-table generation has none. Add the belongs_to by hand, or use spec mode where all tables coexist.

gen.context — schema + context + migration

The macro-less mix phx.gen.context. Takes a leading context name:

gleam run -m gen_context Accounts User users name:string email:string:unique

Generates three files:

It’s standalone-only: the context imports the schema module by name, which is only predictable for a standalone <snake-record> module.

gen.migration — a migration skeleton

lode/migration/gen gives you the pieces (module_source, file_name); wire them into a tiny runnable that supplies a timestamp version and writes the file:

// src/gen_migration.gleam
import lode/migration/gen
import simplifile

pub fn main() {
  let version = current_utc_stamp()        // YYYYMMDDHHMMSS as an Int (e.g. via gleam/time)
  let src = gen.module_source(name: "add_users", version: version)
  let assert Ok(_) =
    simplifile.write(to: "src/" <> gen.file_name("add_users"), contents: src)
}

Note gen.file_name("add_users") is add_users.gleamno timestamp prefix, because a Gleam module name can’t start with a digit. Ordering is by the version inside migration.new(version) and the order of your migrations() list, not the file name.

The field:type[:modifier] reference

Each field argument is name:type, optionally with a modifier.

typemaps toPostgresSQLite
string, textTextTEXTTEXT
integer, intIntegerINTEGERINTEGER
bigintBigIntBIGINTINTEGER
floatFloatDOUBLE PRECISIONREAL
boolean, boolBooleanBOOLEANINTEGER (0/1)
decimalDecimalNUMERICTEXT
dateDateDATETEXT
timeTimeTIMETEXT
naive_datetimeNaiveDatetimeTIMESTAMPTEXT
utc_datetime, datetimeUtcDatetimeTIMESTAMPTZTEXT
uuidUuidUUIDTEXT
map, json, jsonbJsonbJSONBTEXT

Modifiers:

An auto-incrementing id primary key is always added; you don’t list it.

Where files land, and wiring them up

Generated files are written to src/. Unlike Ecto, lode migrations are module values, not files in a priv/ directory — so after generating a migration, add its migration() to your migrations() list (the one you pass to migrator.run). Run migrations with the migrator entrypoint (see Getting Started for the migrate.gleam pattern); on SQLite the migrator picks the dialect automatically (see Testing with Lode).

Spec mode vs. standalone — which?

See the Divergences from Ecto generators entry for how these map to mix ecto.gen.* / mix phx.gen.*.

Search Document