activerecord

package module
v0.0.0-...-1c1ede2 Latest Latest
Warning

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

Go to latest
Published: Jul 17, 2026 License: BSD-3-Clause Imports: 8 Imported by: 0

README

go-ruby-activerecord/activerecord

activerecord — go-ruby-activerecord

Docs License Go Coverage

A pure-Go (no cgo) reimplementation of Rails' ActiveRecord ORM — the query-building, schema-DDL, association, validation, attribute, persistence, transaction, callback, eager-loading and single-table-inheritance layers that turn a model + relation description into SQL, run it, and materialize records, exactly as MRI's activerecord gem does. The one thing that genuinely needs a database — talking to the wire — is an injected Adapter host seam (wired to go-ruby-sqlite3 / go-ruby-pg), so this module stays 100% Ruby- and CGO-free and produces SQL a differential oracle compares to ActiveRecord's own output byte-for-byte.

It is the ORM backend for go-embedded-ruby, a sibling of go-ruby-sequel (whose dialect approach it shares), and a standalone, reusable module.

The database seam. Every SQL string this library produces — relation to_sql, the migration DDL, the association join geometry, the INSERT/UPDATE/ DELETE for a save, the transaction and savepoint control, the eager-load queries and the prepared-statement templates — is rendered deterministically and byte-faithfully to ActiveRecord. The bytes are run through an Adapter the host implements over its driver; connection pooling and the actual socket I/O are the driver's job. Everything else ActiveRecord does around those bytes — validations, the full callback chain, transactions with nested savepoints, statement caching, eager loading and STI instantiation — lives here.

Features

Faithful port of ActiveRecord's SQL generation + validations, validated against the real activerecord gem on every supported platform.

  • Relation — lazy, immutable, chainable: Where (hash / ?- and :name-placeholder strings / IN / BETWEEN / IS NULL), Not, Or, Select, Order (asc/desc, map, raw), Limit/Offset, Group/Having, Joins/LeftJoins, Distinct, From, Merge, named Scopes, and First/Last/Take/Find/FindBy — rendered by ToSQL().
  • Aggregates & DMLCountSQL/SumSQL/AverageSQL/MinimumSQL/ MaximumSQL (grouped or not), PluckSQL, ExistsSQL, InsertSQL, UpdateAllSQL, DeleteAllSQL.
  • Associationsbelongs_to / has_many / has_one / has_and_belongs_to_many and :through, emitting ActiveRecord's exact INNER/LEFT OUTER JOIN … ON geometry (including HABTM join tables and the two-hop through join with source-reflection direction).
  • SchemaCreateTable (+References/Timestamps/NoPrimaryKey), AddColumnSQL, AddIndexSQL (unique, default index_<t>_on_<cols> name), AddForeignKeySQL, and a per-dialect column-type map.
  • Validationspresence / length (min/max/is) / format / numericality (all comparators + only_integer/odd/even) / inclusion / exclusion / uniqueness (a host-seam query) — producing an Errors shaped like ActiveModel::Errors with ActiveRecord's default message text and full_messages order.
  • Attributes — readers/writers, per-column type casting, and dirty tracking (Changed/Changes/AttributeChanged) like ActiveModel::Dirty.
  • PersistenceSave/Create/Update/Destroy/Delete execute the column-ordered INSERT/UPDATE/DELETE (with the RETURNING clause and the created_at/updated_at timestamping ActiveRecord applies), assign the generated primary key, and reset dirty state — all through the Adapter.
  • Callbacks — the full before/after lifecycle for validation, save, create, update, destroy and the transactional after_commit/after_rollback, fired in ActiveSupport's order (before forward, after LIFO) with throw :abort (ErrAbort) halting semantics.
  • Transactions — real BEGIN/COMMIT/ROLLBACK with nested SAVEPOINT active_record_N/RELEASE/ROLLBACK TO, an ErrRollback sentinel, and panic-safe unwinding; a save inside a Transaction automatically runs on a savepoint.
  • Statement cache — a StatementCache of prepared find/find_by statements (… = ? LIMIT ?, $n on postgres) with a PreparedAdapter seam for true driver-level prepares and a transparent inline fallback.
  • Eager loadingIncludes/Preload run ActiveRecord's N+1-avoiding multi-query fetch for belongs_to/has_many/has_one/HABTM/:through and attach the targets to each record.
  • Query interface — executable ToArray/Pluck/Ids/Exists/Count/ First/Last/Take/FindRecord/FindByRecord/FindEach, plus named Scopes, DefaultScope and Unscoped.
  • Single-table inheritanceSTI/Subclass add ActiveRecord's exact type condition (type = 'Admin', or IN (…) over descendants) and instantiate rows as the subclass named by the discriminator column.
  • Migrations — a Migrator runs migrations through the adapter with a schema_migrations version ledger (idempotent up/rollback in a transaction), plus DropTable/RemoveColumn/RenameColumn/ChangeColumnNull/RemoveIndex/ AddTimestamps DDL.
  • Three dialects — SQLite, PostgreSQL and MySQL identifier quoting and value literalization, selectable per model.

CGO-free, dependency-free, 100% test coverage, gofmt + go vet clean, and green across the six 64-bit Go targets (amd64, arm64, riscv64, loong64, ppc64le, s390x) and three OSes.

Install

go get github.com/go-ruby-activerecord/activerecord

Usage

package main

import (
	"fmt"

	ar "github.com/go-ruby-activerecord/activerecord"
)

func main() {
	users := ar.NewModel("User", "users",
		ar.Column{Name: "id", Type: "integer"},
		ar.Column{Name: "name", Type: "string"},
		ar.Column{Name: "age", Type: "integer"},
		ar.Column{Name: "company_id", Type: "bigint"})
	posts := ar.NewModel("Post", "posts",
		ar.Column{Name: "id", Type: "integer"},
		ar.Column{Name: "user_id", Type: "bigint"})
	users.Register(posts).HasMany("posts", "Post")
	posts.Register(users).BelongsTo("user", "User")

	rel := users.
		Where(map[string]any{"age": &ar.Range{Begin: 18, End: 30}}).
		Joins("posts").
		Order(map[string]any{"name": "asc"}).
		Limit(10)

	fmt.Println(rel.ToSQL())
	// SELECT "users".* FROM "users"
	//   INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
	//   WHERE "users"."age" BETWEEN 18 AND 30
	//   ORDER BY "users"."name" ASC LIMIT 10
}
Validations
w := ar.NewModel("Widget", "widgets", ar.Column{Name: "name", Type: "string"})
w.ValidatesPresence("name")

rec := w.Build(map[string]any{}) // no name
errs := rec.Validate()
fmt.Println(errs.FullMessages()) // ["Name can't be blank"]
The adapter (host) seam

Execution is injected. A host implements Adapter over its driver (go-ruby-sqlite3 / go-ruby-pg) and the package's Exists / Count / LoadAll helpers run the rendered SQL through it:

type Adapter interface {
	Execute(sql string) ([]Row, error)
	ExecuteDML(sql string) (affected, lastInsertID int64, err error)
	AdapterName() string // "sqlite3" | "postgresql" | "mysql2"
}

ValidatesUniqueness(attr, exists) takes a callback the host wires to Exists(adapter, relation) so the one query-dependent validator stays behind the seam too. A host that offers driver-level prepared statements additionally implements PreparedAdapter so the statement cache binds values out-of-band; otherwise the binds are transparently inlined.

Persistence, transactions & the lifecycle

Saving, transactions and eager loading run through the same Adapter:

u, _ := users.Create(adapter, map[string]any{"name": "bob", "age": 30})
// BEGIN immediate TRANSACTION
// INSERT INTO "users" ("name","age","created_at","updated_at")
//   VALUES ('bob',30,'…','…') RETURNING "id"    → u.Get("id")
// COMMIT TRANSACTION

ar.Transaction(adapter, func() error {          // nests as SAVEPOINT active_record_1
	u.Update(adapter, map[string]any{"age": 31})
	return ar.ErrRollback                        // ROLLBACK TO SAVEPOINT …
})

posts, _ := ar.LoadIncludes(adapter, users.All().Includes("posts"))
// SELECT "users".* FROM "users"
// SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (…)

Single-table inheritance (base.STI("type"), admin := base.Subclass("Admin")) adds WHERE "users"."type" = 'Admin' to the subclass's queries and instantiates rows as the subclass. Migrations run through a Migrator with an idempotent schema_migrations ledger.

The oracle

The test suite runs the real activerecord gem (version-gated RUBY_VERSION >= "4.0") against an in-memory SQLite connection and compares this package's output byte-for-byte to ActiveRecord's — not just Relation#to_sql, errors.full_messages and the schema DDL, but also the INSERT/UPDATE/DELETE a save issues, the BEGIN/SAVEPOINT/COMMIT control sequence, the prepared find/find_by templates, the eager-load queries and the STI type condition (captured from ActiveSupport::Notifications with prepared statements disabled so bind values inline). The deterministic, Ruby-free golden vectors alone keep coverage at 100%, so the cross-arch (qemu) and Windows CI lanes — where no MRI is present — still pass the gate.

Tests & coverage

GOWORK=off go test -race -coverprofile=cover.out ./...
GOWORK=off go tool cover -func=cover.out | tail -1   # 100.0%

License

BSD-3-Clause — see LICENSE. Copyright the go-ruby-activerecord/activerecord authors.

WebAssembly

Being pure Go (CGO=0), this library also compiles to WebAssembly — both GOOS=js GOARCH=wasm (browser / Node.js) and GOOS=wasip1 GOARCH=wasm (WASI). CI builds both targets on every push, alongside the six 64-bit native/qemu arches.

GOOS=js     GOARCH=wasm go build ./...   # browser / Node
GOOS=wasip1 GOARCH=wasm go build ./...   # WASI (wasmtime, wasmer, wasmedge, …)

Documentation

Overview

Package activerecord is a pure-Go (CGO-free) reimplementation of Rails' ActiveRecord ORM: the interpreter-independent layers that turn a model + relation description into SQL, run it, and materialize records, exactly as MRI's activerecord gem does. Talking to the database is a host seam — an Adapter the host injects (wired to go-ruby-sqlite3 / go-ruby-pg) — so this package is 100% Ruby-free and produces byte-faithful SQL that a differential oracle compares to ActiveRecord's own output across adapters.

Scope

  • Relation: lazy, chainable query building (where/not/or/order/limit/offset/ group/having/joins/left_joins/select/distinct + find/find_by/first/last/ take, aggregates, pluck, exists?) rendered to a per-Dialect SQL string via Relation.ToSQL, byte-faithful to ActiveRecord, plus the executable counterparts (Relation.ToArray/Relation.Pluck/Relation.Exists/…).
  • Schema & migrations: create_table / add_column / add_index / add_foreign_key / drop_table / remove_column / rename_column DDL and a Migrator that runs migrations through the adapter with a schema_migrations ledger.
  • Associations & eager loading: belongs_to / has_many / has_one / has_and_belongs_to_many (and :through) join SQL, plus [Includes]/Preload multi-query materialization.
  • Validations: presence / length / format / numericality / inclusion / exclusion / uniqueness — producing an Errors shaped like ActiveModel::Errors with ActiveRecord's default messages.
  • Attributes & persistence: readers/writers, dirty tracking (changed?/ changes), type casting, and Save/Model.Create/Destroy through the adapter with timestamps and generated-key assignment.
  • Lifecycle: the full before/after callback chain, [Transaction]s with nested savepoints, a prepared-statement StatementCache, and single-table inheritance (Model.STI/Model.Subclass).

Only the database round-trip itself — Adapter.Execute/Adapter.ExecuteDML and connection pooling — is the host's; everything ActiveRecord does around it lives here.

Ruby value model

Attribute and bind values are represented by an [any] drawn from a small, fixed set of Go types so a host (go-embedded-ruby) can map its object graph to and from this package:

Ruby            Go
----            --
nil             nil
true / false    bool
Integer         int, int64, *big.Int
Float           float64, float32
String          string
Symbol          Symbol
Array           []any
Time            time.Time

Index

Constants

This section is empty.

Variables

View Source
var ErrAbort = errors.New("activerecord: callback chain halted")

ErrAbort is the sentinel a before_* callback returns to halt the chain, the pure-Go equivalent of ActiveRecord's `throw :abort`. A halted lifecycle makes the triggering persistence method return (false, nil): a soft failure, not an error.

View Source
var ErrRollback = errors.New("activerecord: transaction rollback")

ErrRollback is the pure-Go equivalent of `raise ActiveRecord::Rollback`: returning it from a Transaction block rolls that transaction back but is not re-raised to the caller (Transaction returns nil).

Functions

func AddColumnSQL

func AddColumnSQL(dialect Dialect, table, name, typ string, opts ...ColOpt) string

AddColumnSQL renders ALTER TABLE ... ADD "col" type (add_column).

func AddForeignKeySQL

func AddForeignKeySQL(dialect Dialect, fromTable, toTable, column, primaryKey, name string) string

AddForeignKeySQL renders ALTER TABLE ... ADD CONSTRAINT ... FOREIGN KEY, matching add_foreign_key's default constraint name and column convention.

func AddIndexSQL

func AddIndexSQL(dialect Dialect, table string, cols []string, unique bool, name string) string

AddIndexSQL renders CREATE [UNIQUE] INDEX. The index name defaults to ActiveRecord's "index_<table>_on_<cols>" convention.

func AddTimestampsSQL

func AddTimestampsSQL(dialect Dialect, table string) []string

AddTimestampsSQL renders the two ALTER TABLE … ADD statements add_timestamps issues (created_at, updated_at as NOT NULL datetimes).

func ChangeColumnNullSQL

func ChangeColumnNullSQL(dialect Dialect, table, column, typ string, null bool) string

ChangeColumnNullSQL renders the ALTER that toggles a column's NOT NULL constraint (change_column_null). Postgres uses SET/DROP NOT NULL; the other dialects use the MODIFY form.

func Count

func Count(a Adapter, r *Relation) (int64, error)

Count runs the relation's COUNT(*) through the adapter and returns the scalar. The single result row's single column is coerced to int64.

func Delete

func Delete(a Adapter, rec *Record) error

Delete removes the record's row with a single DELETE and no callbacks or transaction, matching ActiveRecord's record.delete.

func Destroy

func Destroy(a Adapter, rec *Record) (bool, error)

Destroy deletes the record's row through the adapter inside a transaction, running the destroy callbacks (before_destroy → DELETE → after_destroy). It reports success and any error; a halted before_destroy returns (false, nil).

func DropTableSQL

func DropTableSQL(dialect Dialect, table string) string

DropTableSQL renders DROP TABLE (drop_table).

func Exists

func Exists(a Adapter, r *Relation) (bool, error)

Exists runs the relation's existence probe through the adapter and reports whether a matching row exists (the execution half of exists?). It is the canonical wiring a uniqueness validator's callback uses.

func Pluralize

func Pluralize(word string) string

Pluralize returns the plural form of a lowercase singular English word using ActiveSupport's default inflection rules (the subset a Rails model name exercises): the uncountable and irregular sets first, then the common suffix rules ("y"->"ies" after a consonant, "s"/"x"/"z"/"ch"/"sh"->"es", "o"->"oes" after a consonant), falling back to appending "s". Input is assumed already underscored/lowercased (Tableize's path); a trailing pluralization is a host concern in the query core, so this is the one place that owns it.

func Preload

func Preload(a Adapter, parents []*Record, name string) error

Preload eager-loads the named association for a set of parent records with a single additional query (per hop), attaching the results to each parent. It is a no-op when parents is empty or the association is unknown.

func RemoveColumnSQL

func RemoveColumnSQL(dialect Dialect, table, column string) string

RemoveColumnSQL renders ALTER TABLE … DROP COLUMN (remove_column), the ANSI form postgres/mysql emit.

func RemoveIndexSQL

func RemoveIndexSQL(dialect Dialect, table string, cols []string, name string) string

RemoveIndexSQL renders DROP INDEX (remove_index). The index name defaults to ActiveRecord's index_<table>_on_<cols> convention when not given.

func RenameColumnSQL

func RenameColumnSQL(dialect Dialect, table, from, to string) string

RenameColumnSQL renders ALTER TABLE … RENAME COLUMN … TO … (rename_column).

func Save

func Save(a Adapter, rec *Record, opts ...SaveOption) (bool, error)

Save inserts a new record or updates a changed one through the adapter, running the validation and callback lifecycle in a transaction. It returns whether the record was saved (false on a validation failure or a halted before_* callback, with no error) and any database or callback error.

The lifecycle mirrors ActiveRecord's save:

before_validation → validate → after_validation   (outside the transaction)
BEGIN
  before_save → before_(create|update) → INSERT/UPDATE →
  after_(create|update) → after_save
COMMIT
after_commit                                       (or after_rollback)

func SchemaMigrationsTableSQL

func SchemaMigrationsTableSQL(dialect Dialect) string

SchemaMigrationsTableSQL renders the CREATE TABLE for ActiveRecord's schema_migrations bookkeeping table.

func Tableize

func Tableize(className string) string

Tableize returns the table name ActiveRecord infers for a class name: the demodulized name underscored and pluralized ("User" -> "users", "LineItem" -> "line_items", "Admin::Account" -> "accounts"). It is the inference ActiveRecord::Base does when a model does not set table_name.

func Transaction

func Transaction(a Adapter, fn func() error) (err error)

Transaction runs fn inside a database transaction on the connection, opening a real transaction at the top level and a SAVEPOINT when already nested. It commits (or releases the savepoint) when fn returns nil, and rolls back when fn returns an error or panics. ErrRollback rolls back and is swallowed; any other error rolls back and propagates.

func TransactionDepth

func TransactionDepth(a Adapter) int

TransactionDepth reports how many transactions/savepoints are currently open on the connection (0 when none). It is exposed for hosts and tests that assert nesting.

Types

type Adapter

type Adapter interface {
	// Execute runs a statement that returns rows (a SELECT / ExistsSQL probe)
	// and yields them in order.
	Execute(sql string) ([]Row, error)
	// ExecuteDML runs an INSERT/UPDATE/DELETE and returns the affected row
	// count (and the last insert id where the driver provides it).
	ExecuteDML(sql string) (affected int64, lastInsertID int64, err error)
	// AdapterName reports the driver's ActiveRecord adapter name, used to pick
	// the [Dialect] ("sqlite3", "postgresql", "mysql2").
	AdapterName() string
}

Adapter is the host seam through which this deterministic core reaches a real database. A host wires an implementation backed by go-ruby-sqlite3 or go-ruby-pg (or any driver) and this package hands it the SQL strings it renders; the actual execution, connection pooling and transactions live in the host. Keeping execution behind this interface is what makes the SQL-generation core 100% Ruby- and CGO-free and byte-for-byte testable against the ActiveRecord oracle.

Row is one result row as an ordered column=>value map; the host materializes these into [Record]s via Model.Load.

type AssocKind

type AssocKind int

AssocKind is the macro that declared an association.

const (
	// BelongsTo is belongs_to: this model holds the foreign key.
	BelongsTo AssocKind = iota
	// HasMany is has_many.
	HasMany
	// HasOne is has_one.
	HasOne
	// HABTM is has_and_belongs_to_many (a join table, no model).
	HABTM
)

type AssocOpt

type AssocOpt func(*Association)

AssocOpt configures an association.

func AssociationForeignKey

func AssociationForeignKey(fk string) AssocOpt

AssociationForeignKey overrides the HABTM other-side key column.

func ForeignKey

func ForeignKey(fk string) AssocOpt

ForeignKey sets a non-default foreign key.

func JoinTable

func JoinTable(name string) AssocOpt

JoinTable overrides the HABTM join-table name.

func Through

func Through(name string) AssocOpt

Through sets the intermediate association name for has_many/has_one :through.

type Association

type Association struct {
	Kind AssocKind
	// Name is the association name ("posts", "company").
	Name string
	// ClassName is the target model's Ruby class name ("Post"). For :through it
	// is the final target.
	ClassName string
	// ForeignKey overrides the default foreign-key column.
	ForeignKey string
	// Through, when set, names the intermediate association (has_many :through /
	// has_one :through).
	Through string
	// JoinTable overrides the HABTM join-table name.
	JoinTable string
	// AssociationForeignKey overrides the HABTM other-side key.
	AssociationForeignKey string
}

Association describes one declared association on a model.

type Callback

type Callback func(*Record) error

Callback is a lifecycle hook body. It receives the record and returns nil to continue, ErrAbort to halt a before_* chain (soft failure), or any other error to abort the operation with that error.

type Change

type Change struct {
	Was any
	Now any
}

Change is one attribute's [old, new] pair (ActiveModel::Dirty#changes value).

type ColOpt

type ColOpt func(*colDef)

ColOpt configures a column in a table definition.

func Default

func Default(v Value) ColOpt

Default sets a column default (default: v).

func Limit

func Limit(n int) ColOpt

Limit sets a column limit (limit: n) — currently used for string sizing.

func NotNull

func NotNull() ColOpt

NotNull marks the column NOT NULL (null:false).

type Column

type Column struct {
	Name string
	Type string
}

Column is one column of a model's table: its name and its logical type (used for DDL emission and attribute type-casting). Type is an ActiveRecord type symbol name ("string", "integer", "boolean", "datetime", …).

type Dialect

type Dialect int

Dialect selects identifier quoting and value literalization so the emitted SQL is byte-faithful to what the matching ActiveRecord adapter produces.

const (
	// SQLite mirrors ActiveRecord's sqlite3 adapter: double-quoted identifiers,
	// booleans as TRUE/FALSE, strings single-quoted with ” escaping.
	SQLite Dialect = iota
	// Postgres mirrors ActiveRecord's postgresql adapter: double-quoted
	// identifiers, booleans as TRUE/FALSE, blobs as bytea hex.
	Postgres
	// MySQL mirrors ActiveRecord's mysql2/trilogy adapter: backtick-quoted
	// identifiers, booleans as 1/0, backslash-escaped strings.
	MySQL
)

func DialectByName

func DialectByName(name string) Dialect

DialectByName maps an ActiveRecord adapter name to a Dialect. Unknown names map to SQLite (the deterministic default used by the oracle).

func DialectFor

func DialectFor(a Adapter) Dialect

DialectFor returns the Dialect matching an adapter's AdapterName.

func (Dialect) String

func (d Dialect) String() string

String returns the dialect's canonical adapter name.

type Errors

type Errors struct {
	// contains filtered or unexported fields
}

Errors is the pure-Go shape of ActiveModel::Errors: per-attribute message lists plus the full_messages rendering ("Attr message"). Attributes and messages are kept in declaration/insertion order to match ActiveRecord.

func (*Errors) Add

func (e *Errors) Add(attr, message string)

Add records a message for an attribute (ActiveModel::Errors#add), preserving first-seen attribute order.

func (*Errors) Count

func (e *Errors) Count() int

Count returns the total number of messages.

func (*Errors) Empty

func (e *Errors) Empty() bool

Empty reports whether there are no errors (ActiveModel::Errors#empty?).

func (*Errors) FullMessages

func (e *Errors) FullMessages() []string

FullMessages returns the "Humanized attr message" list, in attribute insertion order, matching ActiveModel::Errors#full_messages.

func (*Errors) Messages

func (e *Errors) Messages() map[string][]string

Messages returns the per-attribute message map (a copy).

func (*Errors) On

func (e *Errors) On(attr string) []string

On returns the messages for one attribute.

type LengthOpts

type LengthOpts struct {
	Minimum *int
	Maximum *int
	Is      *int
}

LengthOpts configures a length validator (nil field = unset).

type Migrator

type Migrator struct {
	// contains filtered or unexported fields
}

Migrator runs schema migrations through an Adapter, recording applied versions in schema_migrations so migrations are idempotent, mirroring ActiveRecord::Migrator.

func NewMigrator

func NewMigrator(a Adapter) *Migrator

NewMigrator returns a Migrator bound to the adapter's connection and dialect.

func (*Migrator) AppliedVersions

func (mg *Migrator) AppliedVersions() ([]string, error)

AppliedVersions returns the migration versions already recorded, in the order the adapter returns them.

func (*Migrator) Dialect

func (mg *Migrator) Dialect() Dialect

Dialect returns the migrator's dialect.

func (*Migrator) EnsureSchemaMigrations

func (mg *Migrator) EnsureSchemaMigrations() error

EnsureSchemaMigrations creates the schema_migrations table once per migrator.

func (*Migrator) Execute

func (mg *Migrator) Execute(sql string) error

Execute runs one DDL/DML statement through the adapter.

func (*Migrator) Migrate

func (mg *Migrator) Migrate(version string, up func(*Migrator) error) (bool, error)

Migrate runs the up migration for version inside a transaction and records the version, unless it is already applied. It returns whether the migration ran.

func (*Migrator) Rollback

func (mg *Migrator) Rollback(version string, down func(*Migrator) error) (bool, error)

Rollback runs the down migration for version inside a transaction and removes the version record, when the version is currently applied. It returns whether the rollback ran.

type Model

type Model struct {
	// Name is the Ruby class name ("User"), used only for validation messages
	// and diagnostics.
	Name string
	// TableName is the resolved SQL table name ("users").
	TableName string
	// PrimaryKey is the primary-key column name ("id").
	PrimaryKey string
	// Dialect selects SQL rendering; defaults to SQLite (zero value).
	Dialect Dialect
	// contains filtered or unexported fields
}

Model describes a mapped ActiveRecord class: its table name, primary key, columns, associations and validations. A host builds one per Ruby model class and hands it to Model.All (or the Where/Order/… shortcuts) to obtain a Relation, and to Model.Build to obtain a validating attribute record.

TableName defaults to the pluralized, underscored class name in Rails; the host supplies the already-resolved table name (pluralization is a host concern), matching how a differential oracle names its tables.

func NewModel

func NewModel(name, table string, columns ...Column) *Model

NewModel returns a Model for class name with the given table and columns. The primary key defaults to "id".

func (*Model) AddColumn

func (m *Model) AddColumn(name, typ string) *Model

AddColumn appends a column (chainable), for hosts that describe the schema incrementally.

func (*Model) AfterCommit

func (m *Model) AfterCommit(fn Callback) *Model

AfterCommit registers an after_commit callback (runs once the outermost transaction commits).

func (*Model) AfterCreate

func (m *Model) AfterCreate(fn Callback) *Model

AfterCreate registers an after_create callback.

func (*Model) AfterDestroy

func (m *Model) AfterDestroy(fn Callback) *Model

AfterDestroy registers an after_destroy callback.

func (*Model) AfterRollback

func (m *Model) AfterRollback(fn Callback) *Model

AfterRollback registers an after_rollback callback (runs when the transaction rolls back).

func (*Model) AfterSave

func (m *Model) AfterSave(fn Callback) *Model

AfterSave registers an after_save callback.

func (*Model) AfterUpdate

func (m *Model) AfterUpdate(fn Callback) *Model

AfterUpdate registers an after_update callback.

func (*Model) AfterValidation

func (m *Model) AfterValidation(fn Callback) *Model

AfterValidation registers an after_validation callback.

func (*Model) All

func (m *Model) All() *Relation

All returns a Relation over the model (ActiveRecord's Model.all), with the model's default_scope and single-table-inheritance type condition applied — the baseline every Model shortcut (Where/Order/…) starts from. Use Model.Unscoped to obtain a relation with neither.

func (*Model) Association

func (m *Model) Association(name string) *Association

Association returns the named association, or nil.

func (*Model) BeforeCreate

func (m *Model) BeforeCreate(fn Callback) *Model

BeforeCreate registers a before_create callback.

func (*Model) BeforeDestroy

func (m *Model) BeforeDestroy(fn Callback) *Model

BeforeDestroy registers a before_destroy callback.

func (*Model) BeforeSave

func (m *Model) BeforeSave(fn Callback) *Model

BeforeSave registers a before_save callback (runs for both create and update).

func (*Model) BeforeUpdate

func (m *Model) BeforeUpdate(fn Callback) *Model

BeforeUpdate registers a before_update callback.

func (*Model) BeforeValidation

func (m *Model) BeforeValidation(fn Callback) *Model

BeforeValidation registers a before_validation callback.

func (*Model) BelongsTo

func (m *Model) BelongsTo(name, className string, opts ...AssocOpt) *Model

BelongsTo declares a belongs_to association. className defaults from name (host handles classification when it differs) — here name is also used as the class-name stem via [singularizeClass].

func (*Model) Build

func (m *Model) Build(attrs map[string]any) *Record

Build returns a new Record with the given initial attributes (Model.new), type-cast per column. A newly-built record has every set attribute considered changed (no persisted original), matching ActiveRecord.

func (*Model) Columns

func (m *Model) Columns() []Column

Columns returns the model's columns in declaration order. The slice must not be mutated.

func (*Model) Create

func (m *Model) Create(a Adapter, attrs map[string]any, opts ...SaveOption) (*Record, error)

Create builds a record from attrs and saves it, returning the record (whose Record.IsPersisted reports success) and any error, matching Model.create.

func (*Model) DefaultScope

func (m *Model) DefaultScope(body func(*Relation) *Relation) *Model

DefaultScope registers the model's default_scope: a refinement applied to every relation started with Model.All (and the Where/Order/… shortcuts). Model.Unscoped bypasses it. Registering twice replaces the previous one, matching a single default_scope declaration.

func (*Model) FindByRecord

func (m *Model) FindByRecord(a Adapter, cond map[string]any) (*Record, error)

FindByRecord returns the first record matching the hash conditions, or nil (Model.find_by). The conditions are applied in sorted key order for a deterministic single query.

func (*Model) FindByStatement

func (m *Model) FindByStatement(attrs ...string) *PreparedStatement

FindByStatement returns the cached prepared statement for Model.find_by over the given attribute names (order-significant): SELECT … WHERE a = ? AND b = ? … LIMIT ?, binding the supplied values then 1.

func (*Model) FindRecord

func (m *Model) FindRecord(a Adapter, id any) (*Record, error)

FindRecord looks up a row by primary key through the cached prepared statement and returns the record, or nil when not found (Model.find, minus the raise).

func (*Model) FindStatement

func (m *Model) FindStatement() *PreparedStatement

FindStatement returns the cached prepared statement for Model.find(id): SELECT … WHERE pk = ? LIMIT ?, binding [id, 1].

func (*Model) HABTM

func (m *Model) HABTM(name, className string, opts ...AssocOpt) *Model

HABTM declares a has_and_belongs_to_many association.

func (*Model) HasColumn

func (m *Model) HasColumn(name string) bool

HasColumn reports whether the model has a column of the given name.

func (*Model) HasMany

func (m *Model) HasMany(name, className string, opts ...AssocOpt) *Model

HasMany declares a has_many association.

func (*Model) HasOne

func (m *Model) HasOne(name, className string, opts ...AssocOpt) *Model

HasOne declares a has_one association.

func (*Model) InsertSQL

func (m *Model) InsertSQL(attrs map[string]any) string

InsertSQL renders an INSERT for one row of attributes, columns in sorted order for determinism: INSERT INTO "table" ("a", "b") VALUES (v1, v2).

func (*Model) Joins

func (m *Model) Joins(names ...any) *Relation

Joins is the Model shortcut for All().Joins(...).

func (*Model) Load

func (m *Model) Load(attrs map[string]any) *Record

Load returns a Record whose attributes are treated as persisted (loaded from the database): no attribute is changed until a subsequent Set, matching a row materialized by ActiveRecord.

func (*Model) LoadSTI

func (m *Model) LoadSTI(row Row) *Record

LoadSTI materializes a row as the subclass named by its type column when the model is an STI root with that subclass registered; otherwise it loads the row as the receiver. It is the instantiation ActiveRecord does when reading rows from a base-class relation.

func (*Model) Order

func (m *Model) Order(cols ...any) *Relation

Order is the Model shortcut for All().Order(...).

func (*Model) Register

func (m *Model) Register(others ...*Model) *Model

Register links a sibling model so association joins can resolve the target table/keys. It is chainable and idempotent.

func (*Model) STI

func (m *Model) STI(column string) *Model

STI marks the model as a single-table-inheritance root discriminated by column (defaulting to "type"). The base class queries every row (no type filter); subclasses created with Model.Subclass add the filter. The column is added to the model if absent.

func (*Model) Scope

func (m *Model) Scope(name string, body func(*Relation) *Relation) *Model

Scope registers a named scope: a function that refines a Relation. Calling Relation.Scope (or the generated shortcut a host wires) applies it.

func (*Model) ScopeNames

func (m *Model) ScopeNames() []string

ScopeNames returns the registered named-scope names in sorted order.

func (*Model) Select

func (m *Model) Select(cols ...any) *Relation

Select is the Model shortcut for All().Select(...).

func (*Model) Subclass

func (base *Model) Subclass(className string) *Model

Subclass returns a new model for an STI subclass named className: it shares the root's table, primary key, dialect, columns, associations and sibling registry, carries className as its type value, and is filtered by that value in queries. The subclass is registered so the root (and every ancestor) can instantiate and filter it.

func (*Model) Unscoped

func (m *Model) Unscoped() *Relation

Unscoped returns a bare relation that ignores the model's default_scope and STI type condition (ActiveRecord's Model.unscoped), the escape hatch for querying every row of the table.

func (*Model) Validate

func (m *Model) Validate(rec *Record) *Errors

Validate runs every validator against rec and returns the collected Errors.

func (*Model) ValidatesExclusion

func (m *Model) ValidatesExclusion(attr string, in []any) *Model

ValidatesExclusion adds an exclusion validator (validates :attr, exclusion:{in:list}).

func (*Model) ValidatesFormat

func (m *Model) ValidatesFormat(attr string, with *regexp.Regexp) *Model

ValidatesFormat adds a format validator (validates :attr, format:{with:re}).

func (*Model) ValidatesInclusion

func (m *Model) ValidatesInclusion(attr string, in []any) *Model

ValidatesInclusion adds an inclusion validator (validates :attr, inclusion:{in:list}).

func (*Model) ValidatesLength

func (m *Model) ValidatesLength(attr string, o LengthOpts) *Model

ValidatesLength adds a length validator (validates :attr, length:{...}).

func (*Model) ValidatesNumericality

func (m *Model) ValidatesNumericality(attr string, o NumericalityOpts) *Model

ValidatesNumericality adds a numericality validator.

func (*Model) ValidatesPresence

func (m *Model) ValidatesPresence(attr string) *Model

ValidatesPresence adds a presence validator (validates :attr, presence:true).

func (*Model) ValidatesUniqueness

func (m *Model) ValidatesUniqueness(attr string, exists func(rec *Record) bool) *Model

ValidatesUniqueness adds a uniqueness validator. Uniqueness needs a database query, which is a host seam: the exists callback reports whether a conflicting row exists (the host runs Relation.ExistsSQL through its Adapter). When exists is nil the validator is skipped (documented).

func (*Model) Where

func (m *Model) Where(cond ...any) *Relation

Where refines the relation with a condition. The shortcut on Model starts a fresh relation.

type NumericalityOpts

type NumericalityOpts struct {
	OnlyInteger     bool
	GreaterThan     *float64
	GreaterThanOrEq *float64
	LessThan        *float64
	LessThanOrEq    *float64
	EqualTo         *float64
	Other           *float64 // other_than
	Odd             bool
	Even            bool
}

NumericalityOpts configures a numericality validator.

type PreparedAdapter

type PreparedAdapter interface {
	Adapter
	// ExecutePrepared runs a prepared statement with positional bind values.
	ExecutePrepared(sql string, binds []any) ([]Row, error)
}

PreparedAdapter is the optional host seam for true driver-level prepared statements. An Adapter that also implements it receives the SQL template and the ordered bind values, keeping the binds out of the SQL string.

type PreparedStatement

type PreparedStatement struct {

	// SQL is the template with dialect bind markers.
	SQL string
	// contains filtered or unexported fields
}

PreparedStatement is a cached SQL template plus its ordered bind slots. It is immutable and safe to reuse across calls and goroutines.

func (*PreparedStatement) Execute

func (s *PreparedStatement) Execute(a Adapter, supplied ...any) ([]*Record, error)

Execute runs the statement with the supplied bind values and materializes the result rows into persisted records. A PreparedAdapter host gets a true prepared execution; a plain Adapter gets the binds inlined into the SQL.

func (*PreparedStatement) ExecuteOne

func (s *PreparedStatement) ExecuteOne(a Adapter, supplied ...any) (*Record, error)

ExecuteOne runs the statement and returns the first materialized record, or nil when no row matched (the shape find/find_by consume).

type Range

type Range struct {
	Begin     Value
	End       Value
	Exclusive bool // true for a...b (exclusive end)
}

Range models a Ruby Range used as a where value (col: a..b), rendered to BETWEEN (inclusive) or the half-open form ActiveRecord emits for exclusive ranges. A nil Begin/End models a beginless/endless range.

type Record

type Record struct {
	// contains filtered or unexported fields
}

Record is a single model instance's attribute set with dirty tracking: it holds the current values and the values loaded from the database, so Changed/Changes report modifications the way ActiveModel::Dirty does. Values are type-cast to the column type on write (Set).

func LoadAll

func LoadAll(a Adapter, r *Relation) ([]*Record, error)

LoadAll runs the relation's SELECT through the adapter and materializes each row into a persisted Record.

func LoadIncludes

func LoadIncludes(a Adapter, r *Relation) ([]*Record, error)

LoadIncludes runs the relation's main SELECT and then eager-loads every association named by Relation.Includes, attaching the targets to the loaded records. It returns the root records (with their associations preloaded).

func (*Record) AttributeChanged

func (r *Record) AttributeChanged(name string) bool

AttributeChanged reports whether one attribute changed (name_changed?).

func (*Record) Attributes

func (r *Record) Attributes() map[string]any

Attributes returns a copy of the current attribute map.

func (*Record) Changed

func (r *Record) Changed() bool

Changed reports whether any attribute differs from its persisted original (ActiveModel::Dirty#changed?).

func (*Record) ChangedAttributeNames

func (r *Record) ChangedAttributeNames() []string

ChangedAttributeNames returns the changed attribute names (name order).

func (*Record) Changes

func (r *Record) Changes() map[string]Change

Changes returns a map of changed attribute => {old, new}, matching ActiveModel::Dirty#changes.

func (*Record) Errors

func (r *Record) Errors() *Errors

Errors returns the validation errors from the most recent Validate/Save/Valid call (an empty Errors set before any run), matching record.errors.

func (*Record) Get

func (r *Record) Get(name string) (any, bool)

Get returns the current attribute value and whether it is set.

func (*Record) IsDestroyed

func (r *Record) IsDestroyed() bool

IsDestroyed reports whether the record was destroyed (ActiveRecord's destroyed?).

func (*Record) IsNewRecord

func (r *Record) IsNewRecord() bool

IsNewRecord reports whether the record has not yet been inserted (ActiveRecord's new_record?).

func (*Record) IsPersisted

func (r *Record) IsPersisted() bool

IsPersisted reports whether the record corresponds to a stored row (ActiveRecord's persisted?): true after Load or a successful insert, false for a freshly Built record and after Destroy.

func (*Record) PreloadedAssociation

func (r *Record) PreloadedAssociation(name string) []*Record

PreloadedAssociation returns the eager-loaded target records for the named association (empty when it was not preloaded), the pure-Go equivalent of reading an association whose target ActiveRecord has already cached.

func (*Record) SaveClean

func (r *Record) SaveClean()

SaveClean snapshots the current attributes as the persisted baseline (what ActiveRecord does after a successful save): subsequent Changes are relative to now.

func (*Record) Set

func (r *Record) Set(name string, v any) *Record

Set writes an attribute, type-casting to the column type. Unknown attributes are stored verbatim (ActiveRecord raises; a host may pre-filter). Returns the receiver for chaining.

func (*Record) Update

func (r *Record) Update(a Adapter, attrs map[string]any, opts ...SaveOption) (bool, error)

Update assigns attrs to a persisted record and saves it (record.update).

func (*Record) Valid

func (r *Record) Valid() bool

Valid reports whether the record passes all validations.

func (*Record) Validate

func (r *Record) Validate() *Errors

Validate runs the model's validators against this record and caches the result on the record (readable via Record.Errors, like record.errors after valid?).

type Relation

type Relation struct {
	// contains filtered or unexported fields
}

Relation is a lazy, immutable, chainable ActiveRecord::Relation. Every refining method (Where, Order, Limit, …) returns a new Relation, leaving the receiver unchanged, mirroring ActiveRecord's copy-on-refine semantics. Relation.ToSQL renders the accumulated relation to a SQL string that is byte-faithful to ActiveRecord's Relation#to_sql for the model's dialect.

func (*Relation) AverageSQL

func (r *Relation) AverageSQL(col string) string

AverageSQL renders AVG("table"."col") (Model...average(:col)).

func (*Relation) Count

func (r *Relation) Count(a Adapter) (int64, error)

Count runs COUNT(*) for the relation and returns the scalar (relation.count).

func (*Relation) CountColumnSQL

func (r *Relation) CountColumnSQL(col string) string

CountColumnSQL renders COUNT("table"."col").

func (*Relation) CountSQL

func (r *Relation) CountSQL() string

CountSQL renders the COUNT(*) query for the relation (Model...count), preserving where/join/group but dropping order and select, as ActiveRecord does for a count without a column.

func (*Relation) DeleteAllSQL

func (r *Relation) DeleteAllSQL() string

DeleteAllSQL renders a DELETE for the relation's scope (Model...delete_all).

func (*Relation) Distinct

func (r *Relation) Distinct(on ...bool) *Relation

Distinct toggles SELECT DISTINCT. Distinct(false) clears it.

func (*Relation) Exists

func (r *Relation) Exists(a Adapter) (bool, error)

Exists runs the existence probe and reports whether any row matches (relation.exists?).

func (*Relation) ExistsSQL

func (r *Relation) ExistsSQL() string

ExistsSQL renders the existence probe ActiveRecord emits for exists?: SELECT 1 AS one FROM "table" ... LIMIT 1.

func (*Relation) Find

func (r *Relation) Find(id any) *Relation

Find scopes to the row with the given primary-key value (Model.find(id)): WHERE "table"."id" = id LIMIT 1.

func (*Relation) FindBy

func (r *Relation) FindBy(cond map[string]any) *Relation

FindBy scopes to the first row matching the hash conditions (Model.find_by(...)): the conditions plus LIMIT 1.

func (*Relation) FindEach

func (r *Relation) FindEach(a Adapter, batchSize int, fn func(*Record) error) error

FindEach loads the relation in primary-key-ordered batches of batchSize and calls fn for each record, stopping early if fn returns an error (relation.find_each). A non-positive batchSize defaults to 1000, matching ActiveRecord.

func (*Relation) First

func (r *Relation) First() *Relation

First returns a relation scoped to the first row: ORDER BY primary key ASC (when no order is set) LIMIT 1, matching Model.first.

func (*Relation) FirstRecord

func (r *Relation) FirstRecord(a Adapter) (*Record, error)

FirstRecord runs the relation scoped to its first row and returns it, or nil when empty (relation.first).

func (*Relation) From

func (r *Relation) From(table string) *Relation

From overrides the FROM table name (ActiveRecord's Model.from("table")), keeping the model's column qualification. An empty name clears the override.

func (*Relation) Group

func (r *Relation) Group(cols ...any) *Relation

Group appends GROUP BY columns (table-qualified bare names, raw strings passed through).

func (*Relation) Having

func (r *Relation) Having(cond ...any) *Relation

Having adds a HAVING fragment (same forms as a string Where).

func (*Relation) Ids

func (r *Relation) Ids(a Adapter) ([]any, error)

Ids runs SELECT of the primary key and returns the id values (relation.ids).

func (*Relation) Includes

func (r *Relation) Includes(names ...any) *Relation

Includes records association names to eager-load when the relation is materialized with LoadIncludes (ActiveRecord's includes/preload). It is chainable and copy-on-write like every relation refinement.

func (*Relation) IncludesNames

func (r *Relation) IncludesNames() []string

IncludesNames returns the association names queued for eager loading.

func (*Relation) Joins

func (r *Relation) Joins(names ...any) *Relation

Joins adds INNER JOINs for the named associations (Symbol/string), rendering the join SQL ActiveRecord emits for belongs_to/has_many/has_one/HABTM/:through.

func (*Relation) Last

func (r *Relation) Last() *Relation

Last returns a relation scoped to the last row: the order reversed (or primary key DESC absent an order) LIMIT 1, matching Model.last.

func (*Relation) LastRecord

func (r *Relation) LastRecord(a Adapter) (*Record, error)

LastRecord runs the relation scoped to its last row and returns it, or nil (relation.last).

func (*Relation) LeftJoins

func (r *Relation) LeftJoins(names ...any) *Relation

LeftJoins adds LEFT OUTER JOINs for the named associations.

func (*Relation) Limit

func (r *Relation) Limit(n int) *Relation

Limit sets LIMIT.

func (*Relation) MaximumSQL

func (r *Relation) MaximumSQL(col string) string

MaximumSQL renders MAX("table"."col") (Model...maximum(:col)).

func (*Relation) Merge

func (r *Relation) Merge(other *Relation) *Relation

Merge combines another relation's where/having/order/group/joins into the receiver (ActiveRecord's relation.merge), appending its clauses.

func (*Relation) MinimumSQL

func (r *Relation) MinimumSQL(col string) string

MinimumSQL renders MIN("table"."col") (Model...minimum(:col)).

func (*Relation) Not

func (r *Relation) Not(cond ...any) *Relation

Not adds negated hash conditions (ActiveRecord's where.not). Only the hash form is supported (matching the common where.not(col: v) usage).

func (*Relation) Offset

func (r *Relation) Offset(n int) *Relation

Offset sets OFFSET.

func (*Relation) Or

func (r *Relation) Or(other *Relation) *Relation

Or ORs the receiver's where-clause with another relation's, matching ActiveRecord's relation.or(other): the two predicate groups are parenthesized and joined by OR. Non-where clauses come from the receiver.

func (*Relation) Order

func (r *Relation) Order(cols ...any) *Relation

Order appends ordering terms. A bare name orders ASC; a map[string]any of name=>"desc"/"asc" (or Symbol values) sets direction; a raw string with a space is passed through.

func (*Relation) Pluck

func (r *Relation) Pluck(a Adapter, cols ...any) ([][]any, error)

Pluck runs the pluck SELECT and returns, per row, the requested column values in order (relation.pluck(:a, :b)). Values are read from the result rows by their bare column names, as adapters key them.

func (*Relation) PluckSQL

func (r *Relation) PluckSQL(cols ...any) string

PluckSQL renders the SELECT for plucking the given columns (Model...pluck(:a,:b)): the qualified columns, keeping where/join/order but no star.

func (*Relation) Scope

func (r *Relation) Scope(name string) *Relation

Scope applies a registered named scope by name.

func (*Relation) Select

func (r *Relation) Select(cols ...any) *Relation

Select sets the projection. Bare column names (string/Symbol) are table-qualified and quoted; a string that is not a plain identifier (contains a space, paren, dot or "*") is treated as a raw SQL expression and passed through, matching ActiveRecord.

func (*Relation) SumSQL

func (r *Relation) SumSQL(col string) string

SumSQL renders SUM("table"."col") (Model...sum(:col)).

func (*Relation) Take

func (r *Relation) Take() *Relation

Take returns a relation with just LIMIT 1 and no imposed order (Model.take).

func (*Relation) TakeRecord

func (r *Relation) TakeRecord(a Adapter) (*Record, error)

TakeRecord runs the relation with LIMIT 1 and returns a row without imposing an order, or nil (relation.take).

func (*Relation) ToArray

func (r *Relation) ToArray(a Adapter) ([]*Record, error)

ToArray runs the relation's SELECT and returns the materialized records (ActiveRecord's to_a / load).

func (*Relation) ToSQL

func (r *Relation) ToSQL() string

ToSQL renders the relation to a SELECT statement, byte-faithful to ActiveRecord's Relation#to_sql for the model's dialect.

func (*Relation) UpdateAllSQL

func (r *Relation) UpdateAllSQL(sets map[string]any) string

UpdateAllSQL renders an UPDATE for the relation's scope, SET assignments in sorted column order (Model...update_all(a: 1)).

func (*Relation) Where

func (r *Relation) Where(cond ...any) *Relation

Where refines the relation. It accepts, matching ActiveRecord:

  • a Hash-like map (map[string]any) — column => value, rendered as equality / IN / BETWEEN / IS NULL and AND-joined and table-qualified.
  • a single string — a raw SQL fragment, wrapped in parens.
  • a string with "?" placeholders followed by bind values — each "?" substituted by the quoted value.
  • a string with ":name" placeholders followed by one map of binds.

The receiver is unchanged; a new Relation is returned.

type Row

type Row = map[string]any

Row is one result row: column name => value.

type SaveOption

type SaveOption func(*saveConfig)

SaveOption configures Save/Create.

func WithoutValidation

func WithoutValidation() SaveOption

WithoutValidation skips validations for this write (save(validate: false)).

type StatementCache

type StatementCache struct {
	// contains filtered or unexported fields
}

StatementCache memoizes prepared statements by a string key (the query shape), mirroring the per-model statement cache ActiveRecord keeps for find/find_by.

func NewStatementCache

func NewStatementCache() *StatementCache

NewStatementCache returns an empty statement cache.

func (*StatementCache) Fetch

func (c *StatementCache) Fetch(key string, build func() *PreparedStatement) *PreparedStatement

Fetch returns the cached statement for key, building and caching it with build on a miss (ActiveRecord's StatementCache.create-then-cache pattern).

type Substitute

type Substitute struct{}

Substitute is ActiveRecord's bind placeholder (StatementCache::Substitute, i.e. `params.bind`): it marks where a value will be supplied at execute time rather than baked into the template.

type Symbol

type Symbol string

Symbol is a Ruby Symbol (`:name`), used for column and association names as a host may hand them over. Its string form is the bare name.

func (Symbol) String

func (s Symbol) String() string

String returns the symbol's bare name.

type TableDef

type TableDef struct {
	// contains filtered or unexported fields
}

TableDef accumulates a create_table definition, emitting the CREATE TABLE DDL ActiveRecord's schema statements produce for the dialect.

func CreateTable

func CreateTable(dialect Dialect, name string) *TableDef

CreateTable begins a create_table definition. By default an "id" integer primary key is added (ActiveRecord's default), matching create_table :name.

func (*TableDef) Column

func (t *TableDef) Column(name, typ string, opts ...ColOpt) *TableDef

Column adds a column of the given type.

func (*TableDef) NoPrimaryKey

func (t *TableDef) NoPrimaryKey() *TableDef

NoPrimaryKey disables the implicit primary key (create_table id:false).

func (*TableDef) PrimaryKey

func (t *TableDef) PrimaryKey(name string) *TableDef

PrimaryKey sets the primary-key column name (create_table primary_key: :name / id: :name), replacing the default "id".

func (*TableDef) References

func (t *TableDef) References(name string, opts ...ColOpt) *TableDef

References adds a "<name>_id" bigint foreign-key column (t.references).

func (*TableDef) Timestamps

func (t *TableDef) Timestamps() *TableDef

Timestamps adds created_at/updated_at NOT NULL datetime columns (t.timestamps), matching ActiveRecord's precision-6 datetimes.

func (*TableDef) ToSQL

func (t *TableDef) ToSQL() string

ToSQL renders the CREATE TABLE statement.

type Value

type Value = any

Value is the interface satisfied by every Ruby value this package handles. It is purely documentary — the public API uses any.

Jump to

Keyboard shortcuts

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