module

PgORM::Persistence

Persistence module handles all database CRUD (Create, Read, Update, Delete) operations.

This module is automatically included in PgORM::Base and provides instance methods for saving, updating, and deleting records, as well as class methods for bulk operations.

Lifecycle States

Records can be in one of three states:

  • New: Not yet saved to database (new_record? == true)
  • Persisted: Saved to database (persisted? == true)
  • Destroyed: Deleted from database (destroyed? == true)

Callbacks

Persistence operations trigger callbacks in this order:

  • Create: before_create → before_save → after_save → after_create
  • Update: before_update → before_save → after_save → after_update
  • Destroy: before_destroy → after_destroy

Instance methods

delete

Deletes the record from the database without callbacks.

This is faster than destroy but:

  • Does NOT run callbacks
  • Does NOT update associations
  • Does NOT wrap in a transaction

Use this for performance-critical deletions where you don't need the full destroy lifecycle.

Example

user = User.find(1)
user.delete # Direct DELETE query

Returns self (even if already destroyed or new).

Source
destroy

Destroys the record, running callbacks and updating associations.

This is the "safe" way to delete records as it:

  • Runs before_destroy and after_destroy callbacks
  • Handles dependent associations (nullify, delete, destroy)
  • Wraps everything in a transaction

Example

user = User.find(1)
user.destroy
user.destroyed? # => true
user.persisted? # => false

Returns self (even if already destroyed or new).

Source
persisted?

Returns true if the record has been saved to the database and not destroyed.

Example

user = User.new(name: "John")
user.persisted? # => false

user.save!
user.persisted? # => true

user.destroy
user.persisted? # => false
Source
reload!

Reloads the record from the database, discarding any changes.

Useful for:

  • Reverting unsaved changes
  • Getting fresh data after external updates
  • Clearing dirty tracking

Example

user = User.find(1)
user.name = "Changed"
user.reload!  # Reverts to database value
user.changed? # => false

Raises

  • PgORM::Error::RecordNotSaved if record was never persisted
  • PgORM::Error::RecordNotFound if record no longer exists in database
Source
save

Saves the record to the database.

For new records, performs an INSERT. For existing records, performs an UPDATE. Returns true if successful, false if validation fails.

Example

user = User.new(name: "John")
if user.save
  puts "Saved! ID: #{user.id}"
else
  puts "Failed: #{user.errors}"
end

# Update existing record
user.name = "Jane"
user.save # => true (if valid)

Callbacks

Triggers appropriate callbacks based on record state:

  • New records: before_create, before_save, after_save, after_create
  • Existing: before_update, before_save, after_save, after_update
Source
save!

Saves the record to the database, raising an exception on failure.

For new records, performs an INSERT. For existing records, performs an UPDATE. Only updates changed attributes (dirty tracking).

Example

user = User.new(name: "John")
user.save! # => #<User id: 1, name: "John">

user.name = "Jane"
user.save! # Only updates 'name' column

Raises

  • PgORM::Error::RecordNotSaved if record was destroyed
  • PgORM::Error::RecordInvalid if validation fails
  • PgORM::Error::RecordNotSaved if database operation fails
Source
update

Updates the record with new attributes and saves it.

Returns true if successful, false if validation fails.

Example

user = User.find(1)
if user.update(name: "Jane", email: "jane@example.com")
  puts "Updated!"
else
  puts "Failed: #{user.errors}"
end
Source
update!

Updates the record with new attributes and saves it, raising on failure.

Example

user = User.find(1)
user.update!(name: "Jane", email: "jane@example.com")

Raises PgORM::Error::RecordInvalid if validation fails.

Source
update_fields

Atomically updates specific fields without running callbacks or validations.

This is faster than update! but bypasses:

  • Validations
  • Callbacks (before_save, after_save, etc.)
  • Dirty tracking (changes are cleared after update)

Use this for performance-critical updates where you don't need the full persistence lifecycle.

Example

user = User.find(1)
user.update_fields(last_login: Time.utc, login_count: 42)
# Direct UPDATE query, no callbacks

Raises Error::RecordNotSaved if called on a new record.

Source