lode/gen/schema

gen.schema — scaffold a schema (and migration) from a Phoenix-style field:type command line, the macro-less equivalent of mix phx.gen.schema.

Product products name:string price:decimal in_stock:boolean
author_id:references:users email:string:unique

Two output modes (both produce the same runtime Schema(row) — they differ only in source-of-truth and maintenance):

migration_source emits the create_table migration for either mode. A table must be registered ONE way — a CLI wrapper enforces the no-overlap guard (don’t spec-feed a table that already has a standalone module, and vice versa).

Types

One parsed field:type[:modifier] argument.

pub type Field {
  Column(name: String, type_: spec.ColumnType, unique: Bool)
  Reference(assoc: String, fk_column: String, table: String)
}

Constructors

  • Column(name: String, type_: spec.ColumnType, unique: Bool)

    A scalar column (name:string, price:decimal:unique).

  • Reference(assoc: String, fk_column: String, table: String)

    A belongs_to reference (author_id:references:users): the foreign-key column, the related table, and the association name (the fk minus _id).

A parsed gen.schema invocation.

pub type Parsed {
  Parsed(
    record: String,
    table: String,
    fields: List(Field),
    standalone: Bool,
  )
}

Constructors

  • Parsed(
      record: String,
      table: String,
      fields: List(Field),
      standalone: Bool,
    )

Values

pub fn context_source(
  p: Parsed,
  schema_module schema_module: String,
) -> String

Render a context module — a thin set of CRUD wrappers over the schema (Ecto/Phoenix’s context): list_<plural>, get_<singular>, create_/update_<singular> (taking a changeset), delete_<singular>, and change_<singular> (the cast builder with this schema’s fields). The schema is imported from schema_module (the module holding its Record + schema() — e.g. "user" for a standalone User).

Param names avoid the snake-record module name and the repo/changeset modules (which a same-named binding would shadow): r, cs, record.

pub fn exists(path path: String) -> Bool

Whether a file already exists — for the no-clobber guard (don’t overwrite a hand-owned standalone module).

pub fn migration_file(p: Parsed) -> String

The migration module’s file name, create_<table>.gleam. Unlike Ecto, the name carries no timestamp prefix: a Gleam module name can’t start with a digit, and ordering is by the version inside migration.new(...) (and the migrations() list), not the file name.

pub fn migration_source(
  p: Parsed,
  version version: Int,
) -> String

Render the create_table migration module for this schema.

pub fn module_file(p: Parsed) -> String

The conventional standalone schema module file name, <snake-record>.gleam.

pub fn now_version() -> Int

A migration version stamped from the current UTC time, YYYYMMDDHHMMSS (Ecto’s migration-timestamp convention). Impure — reads the clock.

pub fn parse(args: List(String)) -> Result(Parsed, String)

Parse [--standalone] <Record> <table> <field:type>....

pub fn spec_snippet(p: Parsed) -> String

Render a spec.table(...) entry (spec mode) to paste into db_spec.gleam.

pub fn standalone_module(p: Parsed) -> Result(String, String)

Render the finished, hand-owned schema module (standalone mode), via codegen.

pub fn to_table_spec(p: Parsed) -> spec.TableSpec

Build the TableSpec for standalone codegen: an auto id serial primary key, then the parsed columns. A references: becomes a plain foreign-key Integer column — not a belongs_to — because codegen validates that an association’s table is in the batch, and a single-table generation has no other tables. (Spec mode keeps the belongs_to; see spec_snippet. In standalone mode, add the belongs_to and the related schema by hand.)

pub fn write(
  path path: String,
  contents contents: String,
) -> Result(Nil, String)

Write a generated file (reuses codegen’s writer, so no extra dependency).

Search Document