gen

package
v0.34.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 26, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package gen turns a module specification into Go source.

It is deterministic and it does not call a model: the same specification produces the same bytes, which is what makes golden files a real test rather than a formality. The specification comes from a YAML file a model wrote or from command-line flags. The generator does not care which, and that is the point -- the model never writes Go.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DefaultEventKey added in v0.12.0

func DefaultEventKey(typeName, aggregate string) string

DefaultEventKey derives the published key from the type and the aggregate: InvoicePaid on invoice becomes "invoice.paid".

func DefaultEventName added in v0.12.0

func DefaultEventName(typeName string) string

DefaultEventName derives the routing key from the type: SendInvoice becomes "send-invoice". It is a default rather than the only option, because the name somebody wants is usually "invoice.send" and only they know that.

func Exported added in v0.12.0

func Exported(name string) string

Exported turns any accepted spelling of a name into the Go type it names: "invoice_line", "invoice-line" and "InvoiceLine" all become InvoiceLine.

func Humanise added in v0.21.0

func Humanise(s string) string

Humanise turns WelcomeEmail into "Welcome email".

func IsExportedIdentifier added in v0.12.0

func IsExportedIdentifier(s string) bool

IsExportedIdentifier reports whether s can be a Go type name in a generated file. A generator that emitted an identifier Go refuses would emit a file that does not compile, which is worse than refusing here.

func Kebab added in v0.21.0

func Kebab(s string) string

Kebab turns WelcomeEmail into welcome-email.

func Merge

func Merge(existing, generated []byte) []byte

Merge carries the custom blocks of the existing file into the newly generated one, in order.

Blocks are matched by position, not by name, which is the honest limitation: reordering the generated file would shuffle them. That is why the marker appears once per file, at the end, where new blocks are appended rather than inserted.

func Normalize added in v0.12.0

func Normalize(name string) string

Normalize accepts the two spellings of one name and returns the module one.

`aru make:module` takes a module name -- purchase_order -- while the habit from elsewhere is to type a class name -- PurchaseOrder. People type the second, and the generator needs the first, so both are accepted and converted here rather than in eight commands.

func TypeList

func TypeList() string

TypeList is the closed set, for error messages.

func Write

func Write(root string, files []File, force bool) (written, skipped []string, err error)

Write writes the files, preserving custom blocks in the ones that already exist. It returns what it wrote and what it skipped.

Types

type Command added in v0.12.0

type Command struct {
	// Name is the type name: CloseInvoices.
	Name string
	// Signature is what the person types: "invoice:close".
	Signature string
	// Description is the one line `aru` prints next to it.
	Description string
	// ModulePath is the project's module path, for the import.
	ModulePath string
}

Command is one console command to write.

func (Command) DescriptionOrDefault added in v0.12.0

func (c Command) DescriptionOrDefault() string

DescriptionOrDefault is the one line the console listing prints.

func (Command) Receiver added in v0.12.0

func (c Command) Receiver() string

Receiver is the one-letter receiver, avoiding the names the signature binds.

func (Command) SignatureOrDefault added in v0.12.0

func (c Command) SignatureOrDefault() string

SignatureOrDefault is what the person types, derived from the name when the flag was left out. It is a method so the CLI can print the same value the generated file carries, instead of computing it a second time.

func (Command) Type added in v0.12.0

func (c Command) Type() string

Type is the struct the command is declared as.

type EnumSpec added in v0.12.0

type EnumSpec struct {
	// Type is the Go type name: "InvoiceStatus".
	Type string
	// Values are the members, in declaration order.
	Values []EnumValue
	// Int backs the enum with an integer instead of a string.
	Int bool
}

EnumSpec is one enum.

func (EnumSpec) Base added in v0.12.0

func (s EnumSpec) Base() string

Base is the underlying type.

func (EnumSpec) Human added in v0.12.0

func (s EnumSpec) Human() string

Human is the type in a sentence: "invoice status".

func (EnumSpec) Names added in v0.12.0

func (s EnumSpec) Names() string

Names lists the stored values, for the error message a parse failure carries.

func (EnumSpec) Path added in v0.12.0

func (s EnumSpec) Path() string

Path is where the file goes.

func (EnumSpec) Validate added in v0.12.0

func (s EnumSpec) Validate() error

Validate reports what is wrong before a file is written.

type EnumValue added in v0.12.0

type EnumValue struct {
	// Name is the value as stored: "draft". It is what a column holds and what
	// every consumer of an event reads, so it is never derived from the constant.
	Name string
	// Number is the stored value when the enum is backed by an integer.
	Number int
	// Type is the enum's type name, so a value can spell its own constant.
	Type string
}

EnumValue is one member of the closed set.

func ParseEnumValues added in v0.12.0

func ParseEnumValues(spec, typeName string, asInt bool) ([]EnumValue, error)

ParseEnumValues reads the --values flag: "draft,sent,paid,void", or "draft=1,sent=2,paid=3" when the numbers have to be pinned.

Why a number can be written down

An --int enum is stored as an integer, so its numbers are the meaning of every row already written. Numbering by position alone made adding a value a data migration nobody was told about: `--values "draft,sent,paid" --force` on a file that had been generated as "draft,paid" moves paid from 2 to 3, and every row holding 2 silently becomes "sent". --force exists precisely so that re-running is how you add a value, so the intended workflow was the one that repointed the data.

Positional numbering is kept for the first run, where there is nothing to repoint and writing "=1,=2,=3" would be ceremony. Mixing the two forms in one flag is refused rather than guessed at: what the person meant by "draft,sent=7,paid" is not knowable, and a wrong guess is the same silent repointing.

func (EnumValue) Const added in v0.12.0

func (v EnumValue) Const() string

Const is the constant that names it: InvoiceStatusDraft.

func (EnumValue) Label added in v0.12.0

func (v EnumValue) Label() string

Label is what a form shows: "Draft".

type EventSpec added in v0.12.0

type EventSpec struct {
	// Type is the Go type: "InvoicePaid". Past tense, always -- it is what the
	// events package requires and what makes a consumer able to read a log.
	Type string
	// Aggregate is what it happened to: "invoice". Required, because an empty
	// Aggregate produces an event no consumer can correlate and no error
	// anywhere reports.
	Aggregate string
	// EventName is the published key: "invoice.paid".
	EventName string
	// Fields are the payload's columns.
	Fields []Field
	// ModulePath is the project's module path.
	ModulePath string
}

EventSpec is one domain event.

What comes out is not a dispatcher: an event here is a row in the outbox, written in the same transaction as the write that caused it, and delivery belongs to the relay. So the generated file is a constructor of events.Event plus the constant that names it.

func (EventSpec) Const added in v0.12.0

func (s EventSpec) Const() string

Const is the name of the constant that carries the published key.

func (EventSpec) NeedsTime added in v0.12.0

func (s EventSpec) NeedsTime() bool

NeedsTime reports whether the payload declares a date or a timestamp.

func (EventSpec) Path added in v0.12.0

func (s EventSpec) Path() string

Path is where the file goes.

func (EventSpec) Validate added in v0.12.0

func (s EventSpec) Validate() error

Validate reports what is wrong before a file is written.

type FactoryField added in v0.12.0

type FactoryField struct {
	GoName string
	GoType string
}

FactoryField is one field of the entity, as the factory sees it.

It carries the Go type and not the DSL type, and that is the decision rather than an oversight: `aru make:model --factory` knows money and date, and `aru make:factory` reads the model and only ever sees int64 and time.Time. A default that depended on the DSL would make the same file come out two ways depending on which command wrote it, which is the definition of two shapes of one thing.

func FieldsFromModel added in v0.12.0

func FieldsFromModel(path, entity string) (fields []FactoryField, tenant bool, err error)

FieldsFromModel reads the entity's fields off app/Models/<Entity>.go.

The model is the schema here -- it is a struct, not a class that discovers its columns at runtime -- so the factory is derived from it rather than declared again. Two sources of truth about one set of columns is how they drift, which is the same reason make:policy reads the tenant off the repository instead of asking for it a second time.

ID, TenantID and CreatedAt are skipped: the first is generated by the repository, the second comes from the Grant, the third from the clock.

func (FactoryField) Column added in v0.12.0

func (f FactoryField) Column() string

Column is the field as the DSL spells it, which is what a generated string default reads like: "reference-1".

func (FactoryField) DefaultExpr added in v0.12.0

func (f FactoryField) DefaultExpr() string

DefaultExpr is the generated value, numbered by n so a batch has distinct values.

It is an index rather than a random draw: a fixture that differs between two runs is a test that fails one time in a hundred, and a test that fails one time in a hundred is a test the team deletes.

func (FactoryField) HasDefault added in v0.12.0

func (f FactoryField) HasDefault() bool

HasDefault reports whether Make generates a value when the caller did not set one. A boolean has no spare value to mean "unset", so it is taken as given.

func (FactoryField) Var added in v0.12.0

func (f FactoryField) Var() string

Var is the local variable the generated Make uses: "DeliveryDate" becomes "deliveryDate".

func (FactoryField) ZeroTest added in v0.12.0

func (f FactoryField) ZeroTest() string

ZeroTest is the condition under which Make generates a value.

type FactorySpec added in v0.12.0

type FactorySpec struct {
	Entity       string
	Tenant       bool
	Fields       []FactoryField
	ModelsImport string
}

FactorySpec is what a factory needs to know, and it is deliberately less than a Module: the Go type of each field, and whether the entity has a tenant.

func (FactorySpec) Human added in v0.12.0

func (s FactorySpec) Human() string

Human is the entity in a sentence: "purchase order".

func (FactorySpec) Humans added in v0.12.0

func (s FactorySpec) Humans() string

Humans is the plural, for the doc comment.

func (FactorySpec) NeedsFmt added in v0.12.0

func (s FactorySpec) NeedsFmt() bool

NeedsFmt reports whether a generated default uses fmt.

func (FactorySpec) Path added in v0.12.0

func (s FactorySpec) Path() string

Path is where the file goes.

func (FactorySpec) Type added in v0.12.0

func (s FactorySpec) Type() string

Type is the generated type name.

func (FactorySpec) Validate added in v0.12.0

func (s FactorySpec) Validate() error

Validate reports what is wrong before a file is written.

type Field

type Field struct {
	Name     string // as written by the user: "full_name"
	Type     Type
	Required bool
	Unique   bool
}

Field is one column of the entity.

func ParseFields

func ParseFields(spec string) ([]Field, error)

ParseFields reads the --fields flag: "name:string!,email:email!u,total:money".

The suffixes are "!" for required and "u" for unique. It is terse because it is typed on a command line; the YAML specification spells the same thing out, and produces the same Module.

func (Field) Bind added in v0.7.0

func (f Field) Bind(receiver string) string

Bind renders the field as an argument to a statement.

A date goes through data.Day, which truncates to midnight UTC. DATE is the one type in the portable subset the engines do not agree about: PostgreSQL drops the time part on write and SQLite keeps it, so the same code returns different values on different engines. Everything else binds as it is.

func (Field) BlueprintMethod added in v0.34.0

func (f Field) BlueprintMethod() string

BlueprintMethod is the Blueprint method that declares this column.

func (Field) Column

func (f Field) Column() string

Column is the column name.

func (Field) Display added in v0.8.0

func (f Field) Display(receiver string) string

Display is the expression that turns the entity's field into the row's.

func (Field) Factory added in v0.12.0

func (f Field) Factory() FactoryField

Factory is how a specification field describes itself to a factory.

func (Field) FormType added in v0.8.0

func (f Field) FormType() string

FormType is how the field is declared in the view's form struct.

Everything is text except a boolean, because a form carries text: the value that comes back after a rejection has to be exactly what the person typed, including the "12,00" that failed to parse. A checkbox is the exception -- it is checked or it is not.

func (Field) FormValue added in v0.8.0

func (f Field) FormValue(receiver string) string

FormValue is the expression that fills the form struct from the entity, for the edit screen.

func (Field) GoName

func (f Field) GoName() string

GoName is the exported Go identifier: "full_name" becomes "FullName".

func (Field) GoType

func (f Field) GoType() string

GoType is the Go type.

func (Field) InputStep added in v0.8.0

func (f Field) InputStep() string

InputStep is the step attribute of a numeric input: cents for money, any for a decimal, and whole units for an integer.

func (Field) InputType added in v0.8.0

func (f Field) InputType() string

InputType is the HTML input type of the field.

func (Field) IsBool added in v0.8.0

func (f Field) IsBool() bool

IsBool reports whether the field is a checkbox in a form.

func (Field) IsEmail

func (f Field) IsEmail() bool

IsEmail reports whether the field gets email validation and normalization.

func (Field) IsFraction added in v0.8.0

func (f Field) IsFraction() bool

IsFraction reports whether the field is parsed with ParseFloat.

func (Field) IsLongText added in v0.8.0

func (f Field) IsLongText() bool

IsLongText reports whether the field is rendered as a textarea.

func (Field) IsString

func (f Field) IsString() bool

IsString reports whether the field is text-like, and therefore gets length validation.

func (Field) IsTime added in v0.8.0

func (f Field) IsTime() bool

IsTime reports whether the field is a date or a timestamp, which are the two that arrive as text and leave as time.Time.

func (Field) IsWholeNumber added in v0.8.0

func (f Field) IsWholeNumber() bool

IsWholeNumber reports whether the field is parsed with ParseInt.

func (Field) Label added in v0.8.0

func (f Field) Label() string

Label is the field as a form label: "supplier_email" becomes "Supplier email".

Sentence case, not Title Case: it is what a form label looks like in an application somebody designed, and Title Case On Every Word is what a generator looks like.

func (Field) MaxLength added in v0.7.0

func (f Field) MaxLength() int

MaxLength is the limit the generated validation enforces.

It agrees with the column: a value that passes validation has to fit, or the rejection comes from the database driver instead of from the validator, in a message about a column rather than about a field somebody filled in.

func (Field) Parse added in v0.8.0

func (f Field) Parse(receiver string) string

Parse is the expression a controller uses to read the field from the request.

The helpers it names are methods on the controller rather than package functions: every module generates into the same package now, and two modules declaring parseInt would not compile.

func (Field) SQLType

func (f Field) SQLType() string

SQLType is the column type, in the portable subset.

func (Field) SQLZero added in v0.29.0

func (f Field) SQLZero() string

SQLZero is the value a column of this type gets when it is added to a table that already has rows.

A column added during a rollout has to be readable by the PREVIOUS binary and by the next one. Adding it nullable with no default, while the scan the same generator emits reads every column straight into its Go type, means that the moment `aru migrate` runs the replicas still on the old binary answer "converting NULL to int is unsupported" on every read of that table, and the ones on the new binary do too until something writes each row.

The two halves have to agree, and a default is the half that needs no second pass over the data.

func (Field) TimeLayout added in v0.8.0

func (f Field) TimeLayout() string

TimeLayout is how a date or a timestamp is written in a form field.

They are the layouts the HTML input types produce: "date" submits 2006-01-02 and "datetime-local" submits 2006-01-02T15:04. Parsing anything else would reject what the browser itself sent.

func (Field) ViewType added in v0.8.0

func (f Field) ViewType() string

ViewType is how the field is declared in the view's row struct.

Dates leave as text, already formatted: a view that formats a time.Time would need the time package imported into a generated file whose imports are fixed, and formatting is a decision about presentation that belongs to the controller anyway.

type File

type File struct {
	Path    string // relative to the project root
	Content []byte
}

File is one generated file.

func Generate

func Generate(m Module) ([]File, error)

Generate produces every file of the module.

It writes nothing: it returns the files, so the caller can show a diff, refuse to overwrite, or write them. A generator that writes as it goes cannot be tested against golden files, and cannot be run twice safely.

func GenerateCommand added in v0.12.0

func GenerateCommand(c Command) ([]File, error)

GenerateCommand writes app/Console/Commands/<Name>.go.

The difference from the usual shape is where the command becomes reachable from. Discovery scans a directory and instantiates what it finds; here the command is a value that routes/console.go returns, because nothing in this framework finds a type by reflection.

The cost is one line to add by hand, and the command prints it. What it buys is that `aru route:list`, the console listing and the compiler all read the same slice: a command that is not in it does not exist, and a command in it with a broken signature does not build.

func GenerateController added in v0.12.0

func GenerateController(s Stub) ([]File, error)

GenerateController produces app/Http/Controllers/<Type>.go.

One file, always: a controller is not a module, and a command that also wrote a view and a migration would be `aru make:module` under another name.

func GenerateListener added in v0.12.0

func GenerateListener(l Listener) ([]File, error)

GenerateListener writes app/Listeners/<Name>.go.

The shape differs from the usual one where the delivery does. In process, an event object is dispatched and a listener subscribes to its class; here the event was written to the outbox in the same transaction as the row it is about, and the relay hands it over after the commit.

So a listener implements events.Publisher and answers a NAME, not a type. That is what lets the producer and the consumer live in different binaries later without either of them changing.

Delivery is at-least-once by design: the relay can hand the same event twice if a publish succeeded and the acknowledgement did not. The generated handler says so and leaves the idempotency where only the application can put it.

func GenerateMiddleware added in v0.12.0

func GenerateMiddleware(s Stub) ([]File, error)

GenerateMiddleware produces app/Http/Middleware/<Type>.go.

func GenerateModel added in v0.12.0

func GenerateModel(m Module, parts ModelParts) ([]File, error)

GenerateModel produces the model, and the parts the flags asked for.

It renders the same templates Generate does: a model written by make:model and a model written by make:module are the same bytes, because they are the same file. A second template would be a second shape of one thing.

What it never writes is a repository. A repository pulls a policy with it -- `aru doctor` reports repository-without-policy as an Error -- and the generated policy denies everything, which pulls a service to issue the Grant. A --repository flag would be `aru make:module` with an arbitrary subset missing, and the mandatory path (validate, Authorize, Grant, Repository) is indivisible by construction.

func GenerateRequest added in v0.12.0

func GenerateRequest(s Stub) ([]File, error)

GenerateRequest produces app/Http/Requests/<Type>.go.

func RenderEnum added in v0.12.0

func RenderEnum(s EnumSpec) (File, error)

RenderEnum produces app/Enums/<Type>.go.

func RenderEvent added in v0.12.0

func RenderEvent(s EventSpec) (File, error)

RenderEvent produces app/Events/<Type>.go.

func RenderFactory added in v0.12.0

func RenderFactory(s FactorySpec) (File, error)

RenderFactory produces database/factories/<Entity>Factory.go.

func RenderJob added in v0.12.0

func RenderJob(s JobSpec) (File, error)

RenderJob produces app/Jobs/<Type>.go.

func RenderMail added in v0.21.0

func RenderMail(s MailSpec) ([]File, error)

RenderMail writes the mailable and its two views.

func RenderMigration added in v0.12.0

func RenderMigration(s MigrationSpec) (File, error)

RenderMigration produces the file. Create picks the template.

func RenderSeeder added in v0.12.0

func RenderSeeder(s SeederSpec) (File, error)

RenderSeeder produces database/seeders/<Entity>Seeder.go.

It depends on no import from the project, and that is what keeps it compiling before the wiring exists: a seeder generated with the repository call already written would not build until the developer had edited seeders.Deps and main.go, and what comes out of the generator has to compile.

type JobSpec added in v0.12.0

type JobSpec struct {
	// Type is the Go type the payload takes: "SendInvoice".
	Type string
	// EventName is the routing key stored in Job.Name: "invoice.send". It is
	// what ties the push to the handler, which is why it is a constant.
	EventName string
	// Fields are the payload's columns, from the closed set of types.
	Fields []Field
	// ModulePath is the project's module path, for the import the command prints.
	ModulePath string
}

JobSpec is one background job.

Two types come out of one command, and that is the only conceptual difference worth explaining: the usual shape is one class that is both the payload and the handler, because a container reinstantiates it from the serialized object and injects the dependencies. There is no container here and no object serialization -- the payload is JSON and the handler is a value with its dependencies in the constructor.

func (JobSpec) Const added in v0.12.0

func (s JobSpec) Const() string

Const is the name of the routing constant.

func (JobSpec) Handler added in v0.12.0

func (s JobSpec) Handler() string

Handler is the type that runs the work.

func (JobSpec) NeedsTime added in v0.12.0

func (s JobSpec) NeedsTime() bool

NeedsTime reports whether the payload declares a date or a timestamp.

func (JobSpec) Path added in v0.12.0

func (s JobSpec) Path() string

Path is where the file goes.

func (JobSpec) Validate added in v0.12.0

func (s JobSpec) Validate() error

Validate reports what is wrong before a file is written.

type Kind added in v0.12.0

type Kind string

Kind is which shape of controller was asked for.

Three shapes, and the set is closed for the same reason the type list is: a generator whose shapes grow on demand becomes a language.

const (
	// KindPlain is a controller with no actions yet, and it is the default.
	KindPlain Kind = "plain"
	// KindResource is the seven actions fhttp.Router.Resource looks for.
	KindResource Kind = "resource"
	// KindInvokable is one action, Handle.
	KindInvokable Kind = "invokable"
)

The closed set.

type Listener added in v0.12.0

type Listener struct {
	// Name is the type name: NotifyAccounting.
	Name string
	// Event is the event name it answers: "invoice.paid". It is the string the
	// producer stored, not a Go type -- the outbox carries names and payloads,
	// so a listener and its event never have to be compiled together.
	Event string
	// ModulePath is the project's module path.
	ModulePath string
}

Listener is one event listener to write.

func (Listener) Answers added in v0.31.0

func (l Listener) Answers() string

Answers is the phrase the generated doc comment reads: the quoted event name, or "every event" when the listener was written without one.

It is a method rather than a conditional in the template because the template is a Go string literal, and the conditional sat in a comment line that a reflow later split in half -- putting a comment marker inside the action and leaving the template unparseable for every run of the command.

func (Listener) EventOrAny added in v0.12.0

func (l Listener) EventOrAny() string

EventOrAny is the event it answers, or the marker for "every event".

func (Listener) Receiver added in v0.12.0

func (l Listener) Receiver() string

Receiver is the short receiver for the methods.

func (Listener) Type added in v0.12.0

func (l Listener) Type() string

Type is the struct the listener is declared as.

type MailSpec added in v0.21.0

type MailSpec struct {
	// Type is the exported Go type: WelcomeEmail.
	Type string
	// ModulePath is the project's, so the generated imports resolve.
	ModulePath string
	// Subject is the subject line. Empty derives one from the type.
	Subject string
	// Fields are what the message carries into its views.
	Fields []Field
}

MailSpec is one mailable to write.

func (MailSpec) SubjectLine added in v0.21.0

func (s MailSpec) SubjectLine() string

SubjectLine is what the envelope carries.

func (MailSpec) View added in v0.21.0

func (s MailSpec) View() string

View is the name the two templates are registered under: mail.welcome-email.

type MigrationSpec added in v0.12.0

type MigrationSpec struct {
	// ID is the immutable identifier: "2026_08_07_000002_add_status_to_invoices".
	// It is what GetName returns, and the whole of the ordering rule.
	ID string
	// Type is the Go type the migration is written as:
	// "AddStatusToInvoices". It embeds BaseMigration and registers itself.
	Type string
	// Table is the table the migration is about.
	Table string
	// Tenant composes the unique constraints and the index with tenant_id.
	Tenant bool
	// Fields are the columns.
	Fields []Field
	// Create picks the template: CREATE TABLE when true, ALTER TABLE ... ADD
	// COLUMN when false.
	Create bool
}

MigrationSpec is one migration file, independent of what asked for it.

`aru make:module`, `aru make:model --migration` and `aru make:migration` all render through this: three commands, one shape of file. A second template would drift on the first change nobody remembered to make twice.

func (MigrationSpec) Path added in v0.12.0

func (s MigrationSpec) Path() string

Path is where the file goes, which the id already decides.

func (MigrationSpec) UniqueFields added in v0.12.0

func (s MigrationSpec) UniqueFields() []Field

UniqueFields returns the columns declared unique.

func (MigrationSpec) Validate added in v0.12.0

func (s MigrationSpec) Validate() error

Validate reports what is wrong with the specification before a file is written. A specification error must never become broken code.

type ModelParts added in v0.12.0

type ModelParts struct{ Migration, Factory bool }

ModelParts is what `aru make:model` was asked to write besides the model.

type Module

type Module struct {
	// Name is the module name as the user typed it: "purchase_order".
	Name   string
	Fields []Field
	// Tenant scopes every query by the Grant's tenant. It is a flag rather than
	// the default because a module can legitimately be global -- but the moment
	// it is set, there is no way to write a query that ignores it.
	Tenant bool
	// Module path of the project, for the generated imports.
	ModulePath string
	// Date is the migration id prefix, e.g. "2026_07_31". It is a field rather
	// than time.Now() so the generator stays deterministic and golden files mean
	// something.
	Date string
	// Sequence is the six-digit half of the migration id.
	//
	// Zero means one, so every existing caller and every golden file keeps the id
	// it has. It exists because two migrations written on the same day need two
	// numbers, and the number is read off the directory rather than off the
	// clock: a timestamp to the second collides when two commands run in the same
	// second, and a number read off the files is the same number on every machine.
	Sequence int
	// Permissions maps an action to the roles allowed to take it, and is what
	// opens the generated Policy.
	//
	// Empty means the policy denies everything, which is what `make:module`
	// produces: a generator that guessed at permissions would ship a hole by
	// default, in every project that ran it. `aru generate` fills this from the
	// specification, where a person or a model said so out loud.
	Permissions map[string][]string
}

Module is the whole specification.

func (Module) BoolFields added in v0.8.0

func (m Module) BoolFields() []Field

BoolFields returns the checkbox fields, which are the ones the form renders with a checked attribute.

func (Module) Controller added in v0.8.0

func (m Module) Controller() string

Controller is the type name of the controller: "PurchaseOrderController".

func (Module) Entity

func (m Module) Entity() string

Entity is the exported type name: "purchase_order" becomes "PurchaseOrder".

func (Module) FirstField added in v0.8.0

func (m Module) FirstField() Field

FirstField is the column the listing links from. It is the first the specification declared, which is the one a person thinks of as the name of the record.

func (Module) FormStruct added in v0.8.0

func (m Module) FormStruct() string

FormStruct is the view struct that carries the form fields.

It is not called FormType, so that reading a template is unambiguous: a field has a FormType -- string or bool -- and the module has a FormStruct.

func (Module) Human added in v0.8.0

func (m Module) Human() string

Human is the entity as a person writes it in a sentence: "purchase order".

func (Module) HumanTitle added in v0.8.0

func (m Module) HumanTitle() string

HumanTitle is Human with a capital initial, for a heading: "Purchase order".

func (Module) Humans added in v0.8.0

func (m Module) Humans() string

Humans is the plural of Human: "purchase orders".

func (Module) HumansTitle added in v0.8.0

func (m Module) HumansTitle() string

HumansTitle is Humans with a capital initial: "Purchase orders".

func (Module) MigrationID added in v0.8.0

func (m Module) MigrationID() string

MigrationID is the immutable identifier of the generated migration.

func (Module) MigrationSpec added in v0.12.0

func (m Module) MigrationSpec() MigrationSpec

MigrationSpec is how a module describes its own create-table migration.

func (Module) MigrationType added in v0.31.0

func (m Module) MigrationType() string

MigrationType is the name of the migration type: CreatePurchaseOrdersTable. It is the name the migration registers itself under in its own init.

func (Module) ModelsImport added in v0.8.0

func (m Module) ModelsImport() string

ModelsImport is the import path of app/Models.

The directories of the tree are CamelCase, as PSR-4 spells them, and the packages inside them are lowercase, as Go spells them. Every generated import is therefore written with an explicit alias, so nobody has to guess which identifier a path called ".../app/Models" binds.

func (Module) NeedsFractionParse added in v0.8.0

func (m Module) NeedsFractionParse() bool

NeedsFractionParse reports whether the controller parses a decimal.

func (Module) NeedsTimeParse added in v0.8.0

func (m Module) NeedsTimeParse() bool

NeedsTimeParse reports whether the controller parses a date or a timestamp, which is the only reason it imports time.

func (Module) NeedsWholeParse added in v0.8.0

func (m Module) NeedsWholeParse() bool

NeedsWholeParse reports whether the controller parses an integer.

func (Module) Plural added in v0.8.0

func (m Module) Plural() string

Plural is the exported plural of the entity: "PurchaseOrders". It names the view data types, which are per page and per module.

func (Module) PoliciesImport added in v0.8.0

func (m Module) PoliciesImport() string

PoliciesImport is the import path of app/Policies.

func (Module) PolicyType added in v0.8.0

func (m Module) PolicyType() string

PolicyType is the type name of the policy.

func (Module) Receiver

func (m Module) Receiver() string

Receiver is the short receiver name used in generated methods.

func (Module) RepositoriesImport added in v0.8.0

func (m Module) RepositoriesImport() string

RepositoriesImport is the import path of app/Repositories.

func (Module) RepositoryType added in v0.8.0

func (m Module) RepositoryType() string

RepositoryType is the type name of the repository.

func (Module) RequestsImport added in v0.8.0

func (m Module) RequestsImport() string

RequestsImport is the import path of app/Http/Requests.

func (Module) Resource added in v0.8.0

func (m Module) Resource() string

Resource is the resource segment: the table with dashes instead of underscores. It names the URL, the route names and the view directory, so all three agree by construction -- "purchase-orders", /purchase-orders, purchase-orders.index.

func (Module) RestFields added in v0.8.0

func (m Module) RestFields() []Field

RestFields is everything after the first, for the remaining table columns.

func (Module) Route

func (m Module) Route() string

Route is the URL prefix of the module.

func (Module) RowStruct added in v0.8.0

func (m Module) RowStruct() string

RowStruct is the view struct that carries one record to the markup.

func (Module) Rules added in v0.6.0

func (m Module) Rules() []Rule

Rules returns the permissions in a fixed order, for the template.

Sorted, because a map ranges differently on every run and the golden files would never match twice.

func (Module) ServiceType added in v0.8.0

func (m Module) ServiceType() string

ServiceType is the type name of the service.

func (Module) ServicesImport added in v0.8.0

func (m Module) ServicesImport() string

ServicesImport is the import path of app/Services.

func (Module) Sortable

func (m Module) Sortable() []Field

Sortable returns the fields that may be used for ordering. Only text and timestamps: a sort field is a column name, and the allowlist is what keeps a column name from the request out of the SQL.

func (Module) StoreRequest added in v0.8.0

func (m Module) StoreRequest() string

StoreRequest is the type name of the request that creates: StorePurchaseOrder.

func (Module) Table

func (m Module) Table() string

Table is the table name, pluralized the simple way. English pluralization has hundreds of exceptions; this handles the common ones and gets out of the way.

func (Module) Unexported added in v0.8.0

func (m Module) Unexported() string

Unexported is the entity with a lowercase initial: "purchaseOrder".

Every module now generates into shared packages -- app/Models, app/Policies, app/Repositories -- so an unexported package-level name has to carry the entity or the second module fails to compile.

func (Module) UniqueFields

func (m Module) UniqueFields() []Field

UniqueFields returns the fields declared unique.

func (Module) UpdateRequest added in v0.8.0

func (m Module) UpdateRequest() string

UpdateRequest is the type name of the request that updates.

func (Module) Validate

func (m Module) Validate() error

Validate reports what is wrong with the specification, before any file is written. A specification error must never become broken code.

func (Module) ViewData added in v0.8.0

func (m Module) ViewData(page string) string

ViewData is the data type of one page: PurchaseOrdersIndexData.

func (Module) ViewName added in v0.8.0

func (m Module) ViewName(page string) string

ViewName is how a page is rendered: purchase-orders.index. It is the path under resources/views with dots, which is how a view is named.

func (Module) ViewsImport added in v0.8.0

func (m Module) ViewsImport() string

ViewsImport is the package the compiled views live in.

storage/framework/views and not resources/views, and that is not a detail: a view is WRITTEN in resources/views/<resource>/show.kyse.go and COMPILED to storage/framework/views/<resource>/show.go, which is the only one of the two the Go compiler ever sees -- the source carries `//go:build kyse` precisely so that it does not.

Importing the source directory produces "build constraints exclude all Go files", which names the right directory for the wrong reason and sends the reader looking for a missing build tag.

One directory per resource, because one directory is one Go package.

func (Module) ViewsPackage added in v0.28.1

func (m Module) ViewsPackage() string

ViewsPackage is the Go package clause of the compiled views.

It is Resource with the hyphens removed, and it exists because Resource has them by design: "purchase-orders" is the URL segment, the directory name and the first half of the view name, and all three want the hyphen. A Go package name cannot have one.

Without this, every module whose name is more than one word generated `package purchase-orders` and the project did not compile -- reported by kyse as a bug in the generator, which it was. `aru make:module thing` never showed it: one word has no separator.

type Rule added in v0.6.0

type Rule struct {
	// Action is the constant name: "View", "Create".
	Action string
	Roles  []string
}

Rule is one action and the roles that may take it.

type SeederSpec added in v0.12.0

type SeederSpec struct {
	// Entity is the exported entity name, with no Seeder suffix: "Invoice".
	Entity string
}

SeederSpec is one seeder.

It carries the entity and nothing else: a seeder has no columns, and what it needs to know -- which repository to call -- is a wiring decision that the command prints rather than a flag it takes.

func (SeederSpec) Human added in v0.12.0

func (s SeederSpec) Human() string

Human is the entity in a sentence: "purchase order".

func (SeederSpec) Humans added in v0.12.0

func (s SeederSpec) Humans() string

Humans is the plural of Human, for the doc comment.

func (SeederSpec) Path added in v0.12.0

func (s SeederSpec) Path() string

Path is where the file goes.

func (SeederSpec) Plural added in v0.12.0

func (s SeederSpec) Plural() string

Plural is the exported plural, which is what the field in Deps is called.

func (SeederSpec) Receiver added in v0.12.0

func (s SeederSpec) Receiver() string

Receiver is the short local name the commented example uses.

func (SeederSpec) Type added in v0.12.0

func (s SeederSpec) Type() string

Type is the generated type name: "InvoiceSeeder".

func (SeederSpec) Validate added in v0.12.0

func (s SeederSpec) Validate() error

Validate reports what is wrong before a file is written.

type Stub added in v0.12.0

type Stub struct {
	// Type is the Go type the file declares: "InvoiceController".
	Type string
	// ModulePath is the project's module path, for the generated imports.
	ModulePath string
	// Resource is the URL segment a controller answers: "invoices". It names
	// the route in the wiring the command prints, never the file.
	Resource string
	// Entity is the name the field on routes.Deps takes: "Invoice". It is the
	// type without its suffix, and it appears in the generated example of the
	// route -- an example naming a field nobody would write is an example that
	// gets copied and then corrected.
	Entity string
	// Kind picks the shape of controller. It is ignored by the other stubs.
	Kind Kind
	// Fields are the columns a request carries. Empty is legitimate: it is the
	// empty stub.
	Fields []Field
}

Stub is one granular file: what `aru make:controller`, `aru make:middleware` and `aru make:request` know, which is deliberately less than a Module.

A Module describes an entity and generates twelve files from it. A Stub describes one file, and the person this is for -- porting an application one class at a time -- asks for one file.

func (Stub) Controller added in v0.12.0

func (s Stub) Controller() string

Controller is the type name, under the name the shared controller template asks for. gen.Module answers the same question, which is what lets one template serve both.

func (Stub) IsInvokable added in v0.12.0

func (s Stub) IsInvokable() bool

IsInvokable reports whether the single Handle action is emitted.

func (Stub) IsResource added in v0.12.0

func (s Stub) IsResource() bool

IsResource reports whether the seven actions are emitted.

func (Stub) NeedsTimeParse added in v0.12.0

func (s Stub) NeedsTimeParse() bool

NeedsTimeParse reports whether the stub declares a date or a timestamp, which is the only reason a generated request imports time.

func (Stub) Validate added in v0.12.0

func (s Stub) Validate() error

Validate reports what is wrong with the stub, before any file is written.

It does not reuse Module.Validate: that one requires snake_case and at least one field, and neither is true of a stub -- a controller has no fields, and its name is a Go type.

type Type

type Type string

Type is a field type. The set is closed by decision, not by omission: a generator whose type list grows on demand becomes a language, and a language has to be maintained forever.

const (
	TypeString    Type = "string"
	TypeText      Type = "text"
	TypeInt       Type = "int"
	TypeDecimal   Type = "decimal"
	TypeMoney     Type = "money"
	TypeBool      Type = "bool"
	TypeDate      Type = "date"
	TypeTimestamp Type = "timestamp"
	TypeUUID      Type = "uuid"
	TypeEmail     Type = "email"
)

The closed set.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL