class

User

Inherits Lustra::Model < Lustra::Model::FullTextSearchable < Lustra::Model::JSONDeserialize < Lustra::Model::Initializer < Lustra::Model::HasFactory < Lustra::Model::Introspection < Lustra::Model::ClassMethods < Lustra::Model::HasScope < Lustra::Model::HasRelations < Lustra::Model::HasValidation < Lustra::Validation::Helper < Lustra::Model::HasSaving < Lustra::Model::HasSerialPkey < Lustra::Model::HasTimestamps < Lustra::Model::HasColumns < Lustra::Model::HasHooks < Lustra::Model::Connection < Lustra::ErrorMessages < Reference < Object

Constants

COLUMNS = {"id" => {type: Int64, primary: true, converter: "Int64", db_column_name: "id", crystal_variable_name: id, presence: false, mass_assign: true}, "telegram_id" => {type: Int64, primary: false, converter: "Int64", db_column_name: "telegram_id", crystal_variable_name: telegram_id, presence: true, mass_assign: true}, "first_name" => {type: String, primary: false, converter: "String", db_column_name: "first_name", crystal_variable_name: first_name, presence: true, mass_assign: true}, "last_name" => {type: String | ::Nil, primary: false, converter: "String", db_column_name: "last_name", crystal_variable_name: last_name, presence: true, mass_assign: true}, "username" => {type: String | ::Nil, primary: false, converter: "String", db_column_name: "username", crystal_variable_name: username, presence: true, mass_assign: true}, "language_code" => {type: String | ::Nil, primary: false, converter: "String", db_column_name: "language_code", crystal_variable_name: language_code, presence: true, mass_assign: true}, "messages_count" => {type: Int32, primary: false, converter: "Int32", db_column_name: "messages_count", crystal_variable_name: messages_count, presence: true, mass_assign: true}, "updated_at" => {type: Time, primary: false, converter: "Time", db_column_name: "updated_at", crystal_variable_name: updated_at, presence: true, mass_assign: true}, "created_at" => {type: Time, primary: false, converter: "Time", db_column_name: "created_at", crystal_variable_name: created_at, presence: true, mass_assign: true}} of Nil => Nil
POLYMORPHISM_SETTINGS = {} of Nil => Nil

Constructors

build(x : NamedTuple) : self

Build a new empty model and fill the columns using the NamedTuple in argument.

Returns the new model

Source
build(x : NamedTuple, &block : self -> Nil) : self

Build a new empty model and fill the columns using the NamedTuple in argument.

Returns the new model

Source
create(x : NamedTuple, &block : self -> Nil) : self

Build and new model and save it. Returns the model.

The model may not be saved due to validation failure; check the returned model errors? and persisted? flags.

Source
create

Build and new model and save it. Returns the model.

The model may not be saved due to validation failure; check the returned model errors? and persisted? flags.

Source
create(x : NamedTuple) : self

Build and new model and save it. Returns the model.

The model may not be saved due to validation failure; check the returned model errors? and persisted? flags.

Source
create

Build and new model and save it. Returns the model.

The model may not be saved due to validation failure; check the returned model errors? and persisted? flags.

Source
create!(x : NamedTuple, &block : self -> Nil) : self

Build and new model and save it. Returns the model.

Returns the newly inserted model Raises an exception if validation failed during the saving process.

Source
create!

Build and new model and save it. Returns the model.

Returns the newly inserted model Raises an exception if validation failed during the saving process.

Source
create!(x : NamedTuple) : self

Build and new model and save it. Returns the model.

Returns the newly inserted model Raises an exception if validation failed during the saving process.

Source
create!

Build and new model and save it. Returns the model.

Returns the newly inserted model Raises an exception if validation failed during the saving process.

Source
new(h : Hash(String, _), cache : Lustra::Model::QueryCache | Nil = nil, persisted = false, fetch_columns = false)
Source
new(json : JSON::Any, cache : Lustra::Model::QueryCache | Nil = nil, persisted = false)
Source
new(t : NamedTuple, persisted = false)
Source

Class methods

build

Build a new empty model and fill the columns using the NamedTuple in argument.

Returns the new model

Source
build

Build a new empty model and fill the columns using the NamedTuple in argument.

Returns the new model

Source
build

Build a new empty model and fill the columns using the NamedTuple in argument.

Returns the new model

Source
columns
Source
connection

Define on which connection the model is living. Useful in case of models living in different databases.

Is set to "default" by default.

See Lustra::SQL#init(URI, *opts) for more information about multi-connections.

Example:

Lustra::SQL.init("postgres://postgres@localhost/database_1")
Lustra::SQL.init("secondary", "postgres://postgres@localhost/database_2")

class ModelA
  include Lustra::Model

  # Performs all the queries on `database_1`
  # self.connection = "default"
  column id : Int32, primary: true, presence: false
  column title : String
end

class ModelB
  include Lustra::Model

  # Performs all the queries on `database_2`
  self.connection = "secondary"

  column id : Int32, primary: true, presence: false
end
connection=(connection : String)

Define on which connection the model is living. Useful in case of models living in different databases.

Is set to "default" by default.

See Lustra::SQL#init(URI, *opts) for more information about multi-connections.

Example:

Lustra::SQL.init("postgres://postgres@localhost/database_1")
Lustra::SQL.init("secondary", "postgres://postgres@localhost/database_2")

class ModelA
  include Lustra::Model

  # Performs all the queries on `database_1`
  # self.connection = "default"
  column id : Int32, primary: true, presence: false
  column title : String
end

class ModelB
  include Lustra::Model

  # Performs all the queries on `database_2`
  self.connection = "secondary"

  column id : Int32, primary: true, presence: false
end
create_from_json(string_or_io : String | IO, trusted : Bool = false)

Create a new model from json and save it. Returns the model.

The model may not be saved due to validation failure; check the returned model's errors? and persisted? flags. Trusted flag set to true will allow mass assignment without protection, FALSE by default

create_from_json!(string_or_io : String | IO, trusted : Bool = false)

Create a new model from json and save it. Returns the model.

Returns the newly inserted model Raises an exception if validation failed during the saving process. Trusted flag set to true will allow mass assignment without protection, FALSE by default

find(ids : Array)

Find multiple models by an array of primary keys. Returns an array of models (may be empty if none found).

users = User.find([1, 2, 3])
users.size # => 0..3 depending on how many were found
Source
find(x)

Returns a model using primary key equality Returns nil if not found.

Source
find!(ids : Array)

Find multiple models by an array of primary keys. Raises error if ANY of the IDs are not found.

users = User.find!([1, 2, 3]) # Raises if any ID is not found
Source
find!(x)

Returns a model using primary key equality. Raises error if the model is not found.

Source
find_by(tuple : NamedTuple)

Find a model by column values. Returns nil if not found. This is an alias for query.find(**tuple) with better naming.

user = User.find_by(email: "test@example.com")
user = User.find_by(first_name: "John", last_name: "Doe")
Source
find_by

Find a model by column values. Returns nil if not found. This is an alias for query.find(**tuple) with better naming.

user = User.find_by(email: "test@example.com")
user = User.find_by(first_name: "John", last_name: "Doe")
Source
find_by!(tuple : NamedTuple)

Find a model by column values. Raises error if not found. This is an alias for query.find!(**tuple) with better naming.

user = User.find_by!(email: "test@example.com")
Source
find_by!

Find a model by column values. Raises error if not found. This is an alias for query.find!(**tuple) with better naming.

user = User.find_by!(email: "test@example.com")
Source
from_json(string_or_io : String | IO, trusted : Bool = false)

Create a new empty model and fill the columns from json. Returns the new model

Trusted flag set to true will allow mass assignment without protection, FALSE by default

full_table_name

Return the fully qualified and escaped name for this table. Add schema if schema is different from 'public' (default schema).

ex: "schema"."table"

Source
import(array : Enumerable(self), on_conflict : Lustra::SQL::InsertQuery -> | Nil = nil)

Import a batch of models in one SQL insert query. Each model must be non-persisted.

on_conflict callback can be optionally enabled to manage database constraints.

Note: Old models are not modified. This method returns a copy of the models as saved in the database.

Example:

users = [User.new(id: 1), User.new(id: 2), User.new(id: 3)]
users = User.import(users)
Source
insert(row : NamedTuple, returning = __pkey__, unique_by = nil, record_timestamps = nil) : Hash(String, Lustra::SQL::Any) | Nil

Insert one row with one SQL statement.

This bypasses model instantiation, validations, and callbacks.

Source
insert_all(rows : Array(NamedTuple), returning = __pkey__, unique_by = nil, record_timestamps = nil) : Array(Hash(String, Lustra::SQL::Any))

Insert many rows with one SQL statement.

This bypasses model instantiation, validations, and callbacks.

By default, duplicate rows are skipped by any unique index PostgreSQL reports during ON CONFLICT DO NOTHING.

Source
none

Return an empty, chainable collection (Rails-like .none). Useful for conditional branches where no records should be returned while keeping query chaining intact.

User.none.where { active == true }.count # => 0
Source
polymorphic?
query

Return a new query SELECT * FROM [my_model_table]. It can be refined after that. Automatically applies default_scope if defined.

Source
read_only=(read_only : Bool)
read_only?
register_counter_cache(association_model : Class, counter_column : String, foreign_key : String)

Register a counter cache for this model using model class

Source
reset_counters(id, *counter_models)

Reset counter cache columns to their correct values. This is useful when counter caches become out of sync due to direct SQL operations.

Example:

User.reset_counters(user.id, Post)
User.reset_counters(user.id, Post, Comment)
Source
schema

Define the current PostgreSQL schema. The value is nil by default, which means no schema is specified during querying, so PostgreSQL uses "public".

This property can be redefined on initialization. Example:

class MyModel
  include Lustra::Model

  self.schema = "my_schema"
end
MyModel.query.to_sql # SELECT * FROM "my_schema"."my_models"
schema=(schema : Lustra::SQL::Symbolic | Nil)

Define the current PostgreSQL schema. The value is nil by default, which means no schema is specified during querying, so PostgreSQL uses "public".

This property can be redefined on initialization. Example:

class MyModel
  include Lustra::Model

  self.schema = "my_schema"
end
MyModel.query.to_sql # SELECT * FROM "my_schema"."my_models"
schema_description

Return the table description without printing.

Source
table

Return the table name configured for this model. By convention, the class name defaults to the pluralized, underscored string form of the model name. Example:

MyModel => "my_models"
Person => "people"
Project::Info => "project_infos"

The property can be updated at initialization to a custom table name:

class MyModel
  include Lustra::Model

  self.table = "another_table_name"
end
MyModel.query.to_sql # SELECT * FROM "another_table_name"
table=(table : Lustra::SQL::Symbolic)

Return the table name configured for this model. By convention, the class name defaults to the pluralized, underscored string form of the model name. Example:

MyModel => "my_models"
Person => "people"
Project::Info => "project_infos"

The property can be updated at initialization to a custom table name:

class MyModel
  include Lustra::Model

  self.table = "another_table_name"
end
MyModel.query.to_sql # SELECT * FROM "another_table_name"
upsert(row : NamedTuple, unique_by : Symbol | String = __pkey__, on_duplicate : Symbol = :update, update_only : Enumerable(Lustra::SQL::Symbolic) | Nil = nil) : self | Nil

Insert or update a single row by conflict target.

This bypasses validations and callbacks, like Rails' upsert. Use save with an on_conflict block when lifecycle hooks are needed.

Source
upsert(row : NamedTuple, unique_by : Array(Lustra::SQL::Symbolic), on_duplicate : Symbol = :update, update_only : Enumerable(Lustra::SQL::Symbolic) | Nil = nil) : self | Nil

Insert or update a single row by array conflict target.

Source
upsert(row : NamedTuple, unique_by : Tuple, on_duplicate : Symbol = :update, update_only : Enumerable(Lustra::SQL::Symbolic) | Nil = nil) : self | Nil

Insert or update a single row by composite conflict target.

Model.upsert({tenant_id: 1, slug: "intro", title: "Intro"}, unique_by: {:tenant_id, :slug})
Source
upsert_all(rows : Array(NamedTuple), unique_by : Symbol | String = __pkey__, on_duplicate : Symbol = :update, update_only : Enumerable(Lustra::SQL::Symbolic) | Nil = nil) : Array(self)

Insert or update many rows by conflict target.

Returns the rows saved by PostgreSQL's RETURNING *.

Source
upsert_all(rows : Array(NamedTuple), unique_by : Array(Lustra::SQL::Symbolic), on_duplicate : Symbol = :update, update_only : Enumerable(Lustra::SQL::Symbolic) | Nil = nil) : Array(self)

Insert or update many rows by array conflict target.

Returns the rows saved by PostgreSQL's RETURNING *.

Source
upsert_all(rows : Array(NamedTuple), unique_by : Tuple, on_duplicate : Symbol = :update, update_only : Enumerable(Lustra::SQL::Symbolic) | Nil = nil) : Array(self)

Insert or update many rows by composite conflict target.

Returns the rows saved by PostgreSQL's RETURNING *.

Source

Instance methods

add_built_association(association_name : String, model : Lustra::Model)

Add a built association to track for autosave

Source
attributes

Attributes used when fetch_columns is true.

built_associations

Track built associations for autosave functionality

built_associations=(built_associations : Hash(String, Array(Lustra::Model)))

Track built associations for autosave functionality

cache
changed

Returns an array of names of all changed attributes.

user.email = "new@test.com"
user.first_name = "John"
user.changed  # => ["email", "first_name"]
changed?

Return true if the model is dirty (e.g. one or more fields have been changed.). Return false otherwise.

changes

Returns a hash of all changed attributes with their [old_value, new_value].

user.email = "new@test.com"
user.first_name = "John"
user.changes  # => {"email" => ["old@test.com", "new@test.com"], "first_name" => [nil, "John"]}
clear_built_associations

Clear all built associations (called after successful save)

Source
clear_change_flags

Reset the changed? flag on all columns

The model behaves as if it is no longer dirty, and calling save would apply no changes.

Returns self

created_at

Returns the value of created_at column or throw an exception if the column is not defined.

created_at=(x : Time)

Setter for created_at column.

created_at_column

Returns the column object used to manage created_at field

See Lustra::Model::Column

first_name

Returns the value of first_name column or throw an exception if the column is not defined.

first_name=(x : String)

Setter for first_name column.

first_name_column

Returns the column object used to manage first_name field

See Lustra::Model::Column

has_built_associations?

Check if there are any pending built associations

Source
id

Returns the value of id column or throw an exception if the column is not defined.

id=(x : Int64)

Setter for id column.

id_column

Returns the column object used to manage id field

See Lustra::Model::Column

invalidate_caching

Force to clean-up the caches for the relations connected to this model.

Source
language_code

Returns the value of language_code column or throw an exception if the column is not defined.

language_code=(x : String | Nil)

Setter for language_code column.

language_code_column

Returns the column object used to manage language_code field

See Lustra::Model::Column

last_name

Returns the value of last_name column or throw an exception if the column is not defined.

last_name=(x : String | Nil)

Setter for last_name column.

last_name_column

Returns the column object used to manage last_name field

See Lustra::Model::Column

messages

The method messages is a has_many relation to Message

messages_count

Returns the value of messages_count column or throw an exception if the column is not defined.

messages_count=(x : Int32)

Setter for messages_count column.

messages_count_column

Returns the column object used to manage messages_count field

See Lustra::Model::Column

reset(h : Hash(Symbol, _))

Set the columns from hash

reset(h : Hash(String, _))

Set the model fields from hash

reset(t : NamedTuple)
reset(from_json : JSON::Any)
reset

reset flavors

reset_counters(*counter_models)

Reset counter cache columns

Example:

user = User.find(1)
user.reset_counters(Post)
user.reset_counters(Post, Comment)
Source
set(h : Hash(Symbol, _))

Set the columns from hash

set(h : Hash(String, _))

Set the model fields from hash

set(t : NamedTuple)
set(from_json : JSON::Any)
set

Set one or multiple columns to a specific value These two are equivalent:

model.set(a: 1)
model.a = 1
set_from_json(string_or_io : String | IO, trusted : Bool = false)

Set the fields from json passed as argument Trusted flag set to true will allow mass assignment without protection, FALSE by default

telegram_id

Returns the value of telegram_id column or throw an exception if the column is not defined.

telegram_id=(x : Int64)

Setter for telegram_id column.

telegram_id_column

Returns the column object used to manage telegram_id field

See Lustra::Model::Column

to_h(full = false) : Hash(String, Lustra::SQL::Any)

Return a hash version of the columns of this model.

to_json(emit_nulls : Bool = false)
to_json(json, emit_nulls = false)
touch(time : Time = Time.local) : Lustra::Model

Updates timestamp columns without triggering validations or callbacks.

user.touch             # Updates updated_at
user.touch(2.days.ago) # Updates updated_at to specific time
Source
touch(column : Symbol | String, time : Time = Time.local) : Lustra::Model

Updates the specified column and updated_at without triggering validations or callbacks.

user.touch(:last_login_at) # Updates last_login_at and updated_at
user.touch(:last_seen_at, 1.hour.ago)
Source
touch(columns : Array(Symbol | String), time : Time = Time.local) : Lustra::Model

Updates multiple timestamp columns without triggering validations or callbacks.

user.touch([:last_login_at, :last_seen_at])
user.touch([:last_login_at, :last_seen_at], 3.days.ago)
Source
touch(*columns, time : Time = Time.local) : Lustra::Model

Updates multiple timestamp columns at once.

user.touch(:last_login_at, :last_seen_at)
user.touch(:last_login_at, :last_seen_at, time: 1.day.ago)
Source
update_from_json(string_or_io : String | IO, trusted : Bool = false)

Set the fields from json passed as argument and call save on the object Trusted flag set to true will allow mass assignment without protection, FALSE by default

update_from_json!(string_or_io : String | IO, trusted : Bool = false)

Set the fields from json passed as argument and call save! on the object Trusted flag set to true will allow mass assignment without protection, FALSE by default

update_h

Generate the hash for an update request (like during save).

updated_at

Returns the value of updated_at column or throw an exception if the column is not defined.

updated_at=(x : Time)

Setter for updated_at column.

updated_at_column

Returns the column object used to manage updated_at field

See Lustra::Model::Column

username

Returns the value of username column or throw an exception if the column is not defined.

username=(x : String | Nil)

Setter for username column.

username_column

Returns the column object used to manage username field

See Lustra::Model::Column

validate_fields_presence

For each column, ensure the column has presence information when needed.

This method is called on validation.

Nested types