Persistence
ORM::ActiveRecord exposes both quiet (return False on failure) and loud
(raise an exception on failure) variants of the persistence methods. Use
whichever fits the situation: forms and user-driven flows usually want the
quiet variants so they can re-render with errors, while scripts, callbacks,
and worker code usually want the loud variants so failures aren't silently
swallowed.
Save and Update
save and update return True on success or False if validation failed.
Inspect errors to see what went wrong.
1 2 3 4 5 6 7 8 | |
save-or-die and update-or-die raise X::RecordInvalid instead. The
exception carries the failing record and a list of human-readable messages.
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
create-or-die is a class-level convenience that builds the record, runs
validations, and raises if it could not be saved.
Build
build constructs an in-memory record without touching the database. It's
the right choice when you want to populate a record, run validations against
it, and only save once everything checks out.
1 2 3 4 5 | |
build with no arguments returns a record with default attributes:
1 2 3 | |
The difference from create: create builds and saves in one step (and
returns whether the save succeeded via the resulting record's id), while
build is purely in-memory. Reach for build when you need to inspect or
mutate the record before deciding to save.
Cloning and Copying
dup returns a new, unsaved copy of the record with id, created_at,
and updated_at cleared. The new instance is fresh: not persisted, not
readonly, not destroyed. Use it when you need to fork a record into a
second row without manually copying every attribute.
1 2 3 4 5 6 7 8 9 10 | |
clone returns a shallow copy that preserves the id and the
readonly flag. Mutating attributes on the clone does not affect the
original.
1 2 3 4 5 | |
becomes(Klass) returns an instance of Klass carrying the receiver's
id and attributes. It is the building block for Single-Table Inheritance
(STI) casts — re-instantiating a row through a different subclass so that
subclass-specific behavior applies.
1 2 | |
becomes-or-die(Klass) does the same and additionally writes the new
class name into the type column when the table has one, mirroring
Rails' becomes! for STI.
1 2 3 | |
Destroy and Delete
destroy removes the record from the database and fires the
before-destroy and after-destroy callbacks. Use it when associated cleanup,
logging, or notifications need to run.
1 2 3 4 5 6 7 8 9 | |
delete issues the DELETE directly without callbacks. Use it when you
want a side-effect-free removal (for example, in tests or in a destroy
callback for a parent record).
1 | |
destroy-all is a class-level convenience that wipes every row in the table
without instantiating records or firing per-row callbacks.
1 | |
Attribute Access
A record exposes its attributes in several complementary ways.
assign-attributes mass-assigns from a hash without saving. It returns the
record so you can chain it.
1 2 3 | |
attributes = %hash is the setter alias that delegates to
assign-attributes.
1 2 | |
read-attribute / write-attribute are explicit getters / setters by name.
1 2 | |
The model also supports indexer access via [] and []=, including the
:exists adverb.
1 2 3 4 | |
is-attribute-present returns True when the attribute exists and has a
non-blank value. It follows the same rules as Rails' present?: zero counts
as present, but False, the empty string, an empty hash, or an empty array
do not.
1 2 | |
has-attribute reports whether a name is a real column on the schema (not
whether a value has been assigned).
1 2 | |
attribute-names returns the list of schema columns, and attributes
returns a hash dump of the current attribute values. The dump is a clone, so
mutating it does not affect the record.
1 2 3 | |
State Predicates
A record reports its lifecycle stage through a small set of predicates.
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
The was-* predicates report on the previous transition. was-new-record
flips to True after the first successful save, then back to False on
the next save (because the next save is an update, not an insert).
was-persisted flips to True after a persisted record is destroyed.
1 2 3 4 5 6 7 8 9 10 11 12 | |
After destroy, the record is frozen: any write path raises
X::FrozenRecord. Reads continue to work.
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
Dirty Tracking
The dirty tracking surface mirrors Rails. A record exposes both what is changed right now (since the last load or save) and what was changed previously (the diff that the last save persisted).
Current changes
1 2 3 4 5 6 7 8 | |
Per-attribute predicates exist in two forms: an explicit one that takes the attribute name, and a dynamic dispatch that builds the method name from it.
1 2 3 4 5 6 7 8 | |
attribute-will-change forces an attribute to be considered dirty even
when its value did not change. The dynamic form is <attr>-will-change.
1 2 | |
Previous changes (after save)
save flushes the current changes into previous-changes, then resets the
in-memory dirty state.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
Restoring & reloading
restore-attributes reverts every in-memory change back to the last saved
value. restore-<attr> (or reset-<attr>) reverts a single attribute.
1 2 3 4 5 6 7 8 9 10 | |
reload re-fetches every column from the database and clears any dirty
state. Use it when another process may have updated the same row.
1 2 | |
Targeted Writes
Sometimes a full save is heavier than the task at hand. The targeted-write
helpers give you finer control over which steps run.
| Method | Validations | Callbacks | Timestamps | Persists |
|---|---|---|---|---|
save / update |
yes | yes | yes | yes |
update-attribute(name, val) |
no | yes | yes | yes |
update-column(name, val) |
no | no | no | yes |
update-columns(%attrs) |
no | no | no | yes |
touch(*@names) |
no | no | yes | yes |
increment(name, n=1) |
- | - | - | no |
increment-or-die(name, n=1) |
no | yes | yes | yes |
decrement(name, n=1) |
- | - | - | no |
decrement-or-die(name, n=1) |
no | yes | yes | yes |
toggle(name) |
- | - | - | no |
toggle-or-die(name) |
no | yes | yes | yes |
update-column / update-columns
Skip everything (validations, callbacks, timestamps) and persist exactly what you ask for. Useful for low-level fixes or background workers that must not trigger user-visible side effects.
1 2 | |
update-attribute
Runs callbacks and bumps timestamps but skips validations. Use it when you need a single attribute persisted regardless of whether the rest of the record is currently valid.
1 | |
touch
touch bumps updated_at (and any extra columns you name) without
modifying anything else.
1 2 | |
touch-all is the relation-level form: it touches every matching row.
1 | |
increment / decrement / toggle
The unsuffixed forms mutate the in-memory value and leave persistence to
you. The -or-die variants persist via update-attribute (so callbacks and
timestamps run, validations do not) and raise X::RecordInvalid on failure.
1 2 3 4 5 6 | |
Bulk Operations
When you need to write many rows in one round-trip — or to skip the validation/callback pipeline entirely — reach for the bulk helpers. They operate on the database directly and return affected row counts (or generated ids), not model instances.
update-all / delete-all
update-all issues a single UPDATE against the rows the relation
matches. It returns the number of affected rows. Validations and
callbacks are skipped; no timestamps are bumped automatically.
1 2 | |
delete-all issues a single set-based DELETE and returns the count of
removed rows. No before-destroy / after-destroy callbacks fire.
1 | |
destroy-by(%conditions) and delete-by(%conditions) are class-level
shortcuts. destroy-by walks the matching records and runs callbacks;
delete-by is the fast set-based form.
1 2 | |
Model.update(@ids, %attrs)
Updates several records by primary key. Each id is loaded, mutated, and
saved through the regular update path — validations and callbacks run
for every record.
1 2 3 4 5 6 7 | |
update-counters
Atomic counter increments / decrements expressed in a single SQL statement. Callbacks do not fire and timestamps are not bumped.
1 2 3 4 | |
insert / insert-all
insert writes a single row, skipping validations and callbacks. If a
unique constraint would be violated, the row is silently skipped (returns
0). insert-or-die lets the database error propagate.
1 2 3 | |
insert-all(@rows) writes many rows in a single statement. It returns
the list of inserted ids. The -or-die variant raises on any conflict.
1 2 3 4 | |
Both forms auto-populate created_at / updated_at if those columns
exist and are not supplied.
upsert / upsert-all
upsert does an INSERT … ON CONFLICT … DO UPDATE. By default it
conflicts on the primary key; pass :unique-by to target a different
unique constraint. Pass :update-cols to limit which columns are
overwritten on conflict (otherwise every supplied column is overwritten).
1 2 3 4 5 6 7 8 | |
upsert-all is the bulk form. It returns the number of affected rows
(inserts plus updates).
1 2 3 4 5 6 7 | |
Save Options
save accepts two opt-out flags:
:!validate— bypass validations.:!touch— skip the automaticcreated_at/updated_atbump.
1 2 | |
Readonly Records
make-readonly marks a record as read-only. Subsequent save, update, or
delete calls raise X::ReadOnlyRecord. is-readonly reports the flag.
1 2 3 4 5 6 7 8 9 10 | |
Automatic Timestamps
If a table has created_at and/or updated_at columns, ORM::ActiveRecord
manages them for you. created_at is set on insert; updated_at is set on
every save (insert and update).
See Migrations » Timestamps for the column setup.