diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 36ddccb..77fd77c 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -1,52 +1,52 @@ # Table of contents -- [Cql](README.md) -- [Guides](guides/README.md) - - [Introduction](guides/introduction.md) - - [Installation](guides/installation.md) - - [Core Concepts](guides/coreconcepts/README.md) - - [DB Schema](guides/coreconcepts/schemas.md) - - [Initializing the Database](guides/coreconcepts/initializing-the-database.md) - - [Altering the Schema](guides/coreconcepts/altering-the-schema.md) - - [Migrations](guides/coreconcepts/migrations.md) - - [CRUD Operations](guides/coreconcepts/crud-operations/README.md) - - [Creating Records](guides/coreconcepts/crud-operations/creating-records.md) - - [Reading Records](guides/coreconcepts/crud-operations/reading-records.md) - - [Updating Records](guides/coreconcepts/crud-operations/updating-records.md) - - [Deleting Records](guides/coreconcepts/crud-operations/deleting-records.md) - - [Patterns](guides/coreconcepts/patterns/README.md) - - [Entity Framework](guides/coreconcepts/patterns/entity-framework.md) - - [Active Record](guides/coreconcepts/patterns/active-record/README.md) - - [Records](guides/coreconcepts/patterns/active-record/records.md) - - [Relations](guides/coreconcepts/patterns/active-record/relations.md) - - [Collections](guides/coreconcepts/patterns/active-record/collections.md) - - [Repository](guides/coreconcepts/patterns/repository.md) -- [Cql API](cql/README.md) - - [enum Cql::Adapter](cql/adapter.md) - - [class Cql::AlterTable](cql/altertable.md) - - [class Cql::Column(T)](cql/column.md) - - [class Cql::Delete](cql/delete.md) - - [class Cql::Error](cql/error.md) - - [class Cql::ForeignKey](cql/foreignkey.md) - - [class Cql::Insert](cql/insert.md) - - [class Cql::Migration](cql/migration.md) - - [class Cql::Migrator](cql/migrator.md) - - [class Cql::PrimaryKey(T)](cql/primarykey.md) - - [alias Cql::PrimaryKeyType](cql/primarykeytype.md) - - [class Cql::Query](cql/query.md) - - [module Cql::Record(T, Pk)](cql/record.md) - - [module Cql::Relations](cql/relations.md) - - [class Cql::Repository(T, Pk)](cql/repository.md) - - [class Cql::Schema](cql/schema.md) - - [class Cql::Table](cql/table.md) - - [class Cql::Update](cql/update.md) - - [class Cql::Index](cql/_index.md) - - [Migrator](cql/migrator/README.md) - - [Cql::Migrator::MigrationRecord](cql/migrator/migrationrecord.md) - - [Relations](cql/relations/README.md) - - [module Cql::Relations::BelongsTo](cql/relations/belongsto.md) - - [class Cql::Relations::Collection(Target, Pk)](cql/relations/collection.md) - - [module Cql::Relations::HasMany](cql/relations/hasmany.md) - - [module Cql::Relations::HasOne](cql/relations/hasone.md) - - [class Cql::Relations::ManyCollection(Target, Through, Pk)](cql/relations/manycollection.md) - - [module Cql::Relations::ManyToMany](cql/relations/manytomany.md) +* [Cql](README.md) +* [Guides](guides/README.md) + * [Introduction](guides/introduction.md) + * [Installation](guides/installation.md) + * [Core Concepts](guides/coreconcepts/README.md) + * [DB Schema](guides/coreconcepts/schemas.md) + * [Initializing the Database](guides/coreconcepts/initializing-the-database.md) + * [Altering the Schema](guides/coreconcepts/altering-the-schema.md) + * [Migrations](guides/coreconcepts/migrations.md) + * [CRUD Operations](guides/coreconcepts/crud-operations/README.md) + * [Creating Records](guides/coreconcepts/crud-operations/creating-records.md) + * [Reading Records](guides/coreconcepts/crud-operations/reading-records.md) + * [Updating Records](guides/coreconcepts/crud-operations/updating-records.md) + * [Deleting Records](guides/coreconcepts/crud-operations/deleting-records.md) + * [Patterns](guides/coreconcepts/patterns/README.md) + * [Entity Framework](guides/coreconcepts/patterns/entity-framework.md) + * [Active Record](guides/coreconcepts/patterns/active-record/README.md) + * [Records](guides/coreconcepts/patterns/active-record/records.md) + * [Relations](guides/coreconcepts/patterns/active-record/relations.md) + * [Collections](guides/coreconcepts/patterns/active-record/collections.md) + * [Repository](guides/coreconcepts/patterns/repository.md) +* [Cql API](cql-api/README.md) + * [CRUD](cql-api/crud/README.md) + * [class Cql::Insert](cql-api/crud/class-cql-insert.md) + * [class Cql::Query](cql-api/crud/class-cql-query.md) + * [class Cql::Delete](cql-api/crud/class-cql-delete.md) + * [class Cql::Update](cql-api/crud/class-cql-update.md) + * [class Cql::Schema](cql-api/class-cql-schema/README.md) + * [class Cql::Column(T)](cql-api/class-cql-schema/class-cql-column-t.md) + * [module Cql::Record(T, Pk)](cql-api/class-cql-schema/module-cql-record-t-pk.md) + * [class Cql::ForeignKey](cql-api/class-cql-schema/class-cql-foreignkey.md) + * [alias Cql::PrimaryKeyType](cql-api/class-cql-schema/alias-cql-primarykeytype.md) + * [class Cql::Table](cql-api/class-cql-schema/class-cql-table.md) + * [class Cql::AlterTable](cql-api/class-cql-schema/class-cql-altertable.md) + * [class Cql::PrimaryKey(T)](cql-api/class-cql-schema/class-cql-primarykey-t.md) + * [class Cql::Index](cql-api/class-cql-schema/class-cql-index.md) + * [enum Cql::Adapter](cql-api/class-cql-schema/enum-cql-adapter.md) + * [class Cql::Repository(T, Pk)](cql-api/class-cql-repository-t-pk.md) + * [class Cql::Error](cql-api/class-cql-error.md) + * [module Cql::Relations](cql-api/module-cql-relations/README.md) + * [module Cql::Relations::BelongsTo](cql-api/module-cql-relations/module-cql-relations-belongsto.md) + * [class Cql::Relations::Collection(Target, Pk)](cql-api/module-cql-relations/class-cql-relations-collection-target-pk.md) + * [module Cql::Relations::HasMany](cql-api/module-cql-relations/module-cql-relations-hasmany.md) + * [module Cql::Relations::HasOne](cql-api/module-cql-relations/module-cql-relations-hasone.md) + * [class Cql::Relations::ManyCollection(Target, Through, Pk)](cql-api/module-cql-relations/class-cql-relations-manycollection-target-through-pk.md) + * [module Cql::Relations::ManyToMany](cql-api/module-cql-relations/module-cql-relations-manytomany.md) + * [Migrations](cql-api/migrations/README.md) + * [Cql::Migrator::MigrationRecord](cql-api/migrations/cql-migrator-migrationrecord.md) + * [class Cql::Migrator](cql-api/migrations/class-cql-migrator.md) + * [class Cql::Migration](cql-api/migrations/class-cql-migration.md) diff --git a/docs/cql-api/README.md b/docs/cql-api/README.md new file mode 100644 index 0000000..a825673 --- /dev/null +++ b/docs/cql-api/README.md @@ -0,0 +1,2 @@ +# Cql API + diff --git a/docs/cql-api/class-cql-error.md b/docs/cql-api/class-cql-error.md new file mode 100644 index 0000000..dd489d4 --- /dev/null +++ b/docs/cql-api/class-cql-error.md @@ -0,0 +1,15 @@ +# class Cql::Error + +`Exception` < `Reference` < `Object` + +Error class This class represents an error in the Cql library It provides a message describing the error + +**Example** Raising an error + +```crystal +raise Cql::Error.new("Something went wrong") +``` + +## Constructors + +### def new`(message : String)` diff --git a/docs/cql-api/class-cql-repository-t-pk.md b/docs/cql-api/class-cql-repository-t-pk.md new file mode 100644 index 0000000..1be517b --- /dev/null +++ b/docs/cql-api/class-cql-repository-t-pk.md @@ -0,0 +1,297 @@ +# class Cql::Repository(T, Pk) + +`Reference` < `Object` + +A repository for a specific table This class provides a high-level interface for interacting with a table It provides methods for querying, creating, updating, and deleting records It also provides methods for pagination and counting records + +**Example** Creating a new repository + +```crystal +class UserRepository < Cql::Repository(User) + def initialize(@schema : Schema, @table : Symbol) +end + +user_repo = UserRepository.new(schema, :users) +user_repo.all +user_repo.find(1) +``` + +## Constructors + +### def new`(schema : Schema, table : Symbol)` + +Initialize the repository with a schema and table name + +* **@param** schema \[Schema] The schema to use +* **@param** table \[Symbol] The name of the table +* **@return** \[Repository] The repository object + +**Example** Creating a new repository + +```crystal +class UserRepository < Cql::Repository(User) +end + +user_repo = UserRepository.new(schema, :users +``` + +## Instance Methods + +### def all + +Fetch all records of type T + +* **@return** \[Array(T)] The records + +**Example** Fetching all records + +```crystal +user_repo.all +``` + +### def build`(attrs : Hash(Symbol, DB::Any))` + +Build a new object of type T with the given attributes + +* **@param** attrs \[Hash(Symbol, DB::Any)] The attributes to use +* **@return** \[T] The new object + +**Example** Building a new user object + +```crystal +user_repo.build(name: "Alice", email: " [email protected]") +``` + +### def count + +Count all records in the table + +* **@return** \[Int64] The number of records + +**Example** Counting all records + +```crystal +user_repo.count +``` + +### def create`(attrs : Hash(Symbol, DB::Any))` + +Create a new record with given attributes + +* **@param** attrs \[Hash(Symbol, DB::Any)] The attributes to use +* **@return** \[PrimaryKey] The ID of the new record **Example** Creating a new record + +```crystal +user_repo.create(name: "Alice", email: " [email protected]") +``` + +### def create + +### def delete`(id : Pk)` + +Delete a record by ID + +* **@param** id \[PrimaryKey] The ID of the record + +**Example** Deleting a record by ID + +```crystal +user_repo.delete(1) +``` + +### def delete + +### def delete\_all + +Delete all records in the table + +**Example** Deleting all records + +```crystal +user_repo.delete_all +``` + +### def delete\_by + +Delete records matching specific fields + +* **@param** fields \[Hash(Symbol, DB::Any)] The fields to match + +**Example** Deleting records by email + +```crystal +user_repo.delete_by(email: " [email protected]") +``` + +### def exists? + +Check if records exist matching specific fields + +* **@param** fields \[Hash(Symbol, DB::Any)] The fields to match +* **@return** \[Bool] True if records exist, false otherwise + +**Example** Checking if a record exists by email + +```crystal +user_repo.exists?(email: " [email protected]") +``` + +### def find`(id : Pk)` + +Find a record by ID, return nil if not found + +* **@param** id \[PrimaryKey] The ID of the record +* **@return** \[T?] The record, or nil if not found + +**Example** Fetching a record by ID + +```crystal +user_repo.find(1) +``` + +### def find!`(id : Pk)` + +Find a record by ID, raise an error if not found + +* **@param** id \[PrimaryKey] The ID of the record +* **@return** \[T] The record + +**Example** Fetching a record by ID + +```crystal +user_repo.find!(1) +``` + +### def find\_all\_by + +Find all records matching specific fields + +* **@param** fields \[Hash(Symbol, DB::Any)] The fields to match +* **@return** \[Array(T)] The records + +**Example** Fetching all active users + +```crystal +user_repo.find_all_by(active: true) +``` + +### def find\_by + +Find a record by specific fields + +* **@param** fields \[Hash(Symbol, DB::Any)] The fields to match +* **@return** \[T?] The record, or nil if not found + +**Example** Fetching a record by email + +```crystal +user_repo.find_by(email: " [email protected]") +``` + +### def first + +Fetch the first record in the table + +* **@return** \[T?] The first record, or nil if the table is empty + +**Example** Fetching the first record + +```crystal +user_repo.first +``` + +### def insert + +### def last + +Fetch the last record in the table + +* **@return** \[T?] The last record, or nil if the table is empty + +**Example** Fetching the last record + +```crystal +user_repo.last +``` + +### def page`(page_number, per_page = 10)` + +Paginate results based on page number and items per page + +* **@param** page\_number \[Int32] The page number to fetch +* **@param** per\_page \[Int32] The number of items per page +* **@return** \[Array(T)] The records for the page + +**Example** Paginating results + +```crystal +user_repo.page(1, 10) +``` + +### def per\_page`(per_page)` + +Limit the number of results per page + +* **@param** per\_page \[Int32] The number of items per page +* **@return** \[Array(T)] The records for the page + +**Example** Limiting results per page + +```crystal +user_repo.per_page(10) +``` + +### def query + +### def update`(id : Pk, attrs : Hash(Symbol, DB::Any))` + +Update a record by ID with given attributes + +* **@param** id \[PrimaryKey] The ID of the record +* **@param** attrs \[Hash(Symbol, DB::Any)] The attributes to update + +**Example** Updating a record by ID + +```crystal +user_repo.update(1, active: true) +``` + +### def update`(id : Pk, **fields)` + +Update a record by ID with given fields + +* **@param** id \[PrimaryKey] The ID of the record +* **@param** fields \[Hash(Symbol, DB::Any)] The fields to update + +**Example** Updating a record by ID + +```crystal +user_repo.update(1, active: true) +``` + +### def update + +### def update\_all`(attrs : Hash(Symbol, DB::Any))` + +Update all records with given attributes + +* **@param** attrs \[Hash(Symbol, DB::Any)] The attributes to update + +**Example** Updating all records + +```crystal +user_repo.update_all(active: true) +``` + +### def update\_by`(where_attrs : Hash(Symbol, DB::Any), update_attrs : Hash(Symbol, DB::Any))` + +Update records matching where attributes with update attributes + +* **@param** where\_attrs \[Hash(Symbol, DB::Any)] The attributes to match +* **@param** update\_attrs \[Hash(Symbol, DB::Any)] The attributes to update + +**Example** Updating records by email + +```crystal +user_repo.update_by(email: " [email protected]", active: true) +``` diff --git a/docs/cql-api/class-cql-schema/README.md b/docs/cql-api/class-cql-schema/README.md new file mode 100644 index 0000000..b172c09 --- /dev/null +++ b/docs/cql-api/class-cql-schema/README.md @@ -0,0 +1,219 @@ +# class Cql::Schema + +`Reference` < `Object` + +The `Schema` class represents a database schema. + +This class provides methods to build and manage a database schema, including creating tables, executing SQL statements, and generating queries. + +**Example** Creating a new schema + +```crystal +schema = Cql::Schema.define(:northwind, "sqlite3://db.sqlite3") do + table :users do + primary :id, Int64, auto_increment: true + column :name, String + column :email, String + end +end +``` + +**Example** Executing a SQL statement + +```crystal +schema.exec("CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT)") +``` + +**Example** Creating a new query + +```crystal +query = schema.query +``` + +The `Schema` class represents a database schema. + +## Constants + +### Log + +```crystal +::Log.for(self) +``` + +## Constructors + +### def new`(name : Symbol, uri : String, adapter : Adapter = Adapter::Sqlite, version : String = "1.0")` + +Initializes a new schema. + +* **@param** name \[Symbol] the name of the schema +* **@param** uri \[String] the URI of the database +* **@param** adapter \[Adapter] the database adapter (default: `Adapter::Sqlite`) +* **@param** version \[String] the version of the schema (default: "1.0") + +**Example** Initializing a new schema + +```crystal +schema = Cql::Schema.new(:northwind, "sqlite3://db.sqlite3") +``` + +## Class Methods + +### def define`(name : Symbol, uri : String, adapter : Adapter = Adapter::Sqlite, version : String = "1.0", &)` + +Builds a new schema. + +* **@param** name \[Symbol] the name of the schema +* **@param** uri \[String] the URI of the database +* **@param** adapter \[Adapter] the database adapter (default: `Adapter::Sqlite`) +* **@param** version \[String] the version of the schema (default: "1.0") +* **@yield** \[Schema] the schema being built +* **@return** \[Schema] the built schema + +**Example** + +```crystal +schema = Cql::Schema.define(:northwind, "sqlite3://db.sqlite3") do |s| + s.create_table :users do + primary :id, Int64, auto_increment: true + column :name, String + column :email, String + end +end +``` + +## Instance Methods + +### def adapter + +* **@return** \[Adapter] the database adapter (default: `Adapter::Sqlite`) + +### def alter`(table_name : Symbol, &)` + +Alter a table in the schema. + +* **@param** table\_name \[Symbol] the name of the table +* **@yield** \[AlterTable] the table being altered **Example** + +```crystal +schema.alter(:users) do |t| + t.add_column :age, Int32 +end +``` + +**Example** + +```crystal +schema.alter(:users) do |t| + t.drop_column :age +end +``` + +### def db + +* **@return** \[DB::Connection] the database connection + +### def delete + +Creates a new delete query for the schema. + +* **@return** \[Delete] the new delete query **Example** + +```crystal +delete = schema.delete +``` + +### def exec`(sql : String)` + +Executes a SQL statement. + +* **@param** sql \[String] the SQL statement to execute + +**Example** + +```crystal +schema.exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)") +``` + +### def gen + +* **@return** \[Expression::Generator] the expression generator + +### def insert + +Creates a new insert query for the schema. + +* **@return** \[Insert] the new insert query **Example** + +```crystal +insert = schema.insert +``` + +### def migrator + +Creates a new migrator for the schema. + +* **@return** \[Migrator] the new migrator **Example** + +```crystal +migrator = schema.migrator +``` + +### def name + +* **@return** \[Symbol] the name of the schema + +### def query + +Creates a new query for the schema. + +* **@return** \[Query] the new query + +**Example** + +```crystal +query = schema.query +``` + +### def table`(name : Symbol, as as_name = nil, &)` + +Creates a new table in the schema. + +* **@param** name \[Symbol] the name of the table +* **@param** as\_name \[Symbol] the alias of the table +* **@yield** \[Table] the table being created +* **@return** \[Table] the created table **Example** + +```crystal +schema.create_table :users do + primary :id, Int64, auto_increment: true + column :name, String + column :email, String +end +``` + +### def tables + +* **@return** \[Hash(Symbol, Table)] the tables in the schema + +### def update + +Creates a new update query for the schema. + +* **@return** \[Update] the new update query **Example** + +```crystal +update = schema.update +``` + +### def uri + +* **@return** \[String] the URI of the database + +### def version + +* **@return** \[String] the version of the schema + +## Macros + +### macro method\_missing`(call)` diff --git a/docs/cql-api/class-cql-schema/alias-cql-primarykeytype.md b/docs/cql-api/class-cql-schema/alias-cql-primarykeytype.md new file mode 100644 index 0000000..318e84e --- /dev/null +++ b/docs/cql-api/class-cql-schema/alias-cql-primarykeytype.md @@ -0,0 +1,3 @@ +# alias Cql::PrimaryKeyType + +Represents a database primary key column type. diff --git a/docs/cql-api/class-cql-schema/class-cql-altertable.md b/docs/cql-api/class-cql-schema/class-cql-altertable.md new file mode 100644 index 0000000..ab0265d --- /dev/null +++ b/docs/cql-api/class-cql-schema/class-cql-altertable.md @@ -0,0 +1,166 @@ +# class Cql::AlterTable + +`Reference` < `Object` + +This module is part of the Cql namespace and is responsible for handling database alterations. This class represents an AlterTable object. + +**Example** : + +```crystal +alter_table = AlterTable.new +alter_table.add_column(:email, "string") +alter_table.drop_column(:age) +alter_table.rename_column(:email, :user_email) +alter_table.change_column(:age, "string") + +=> # +``` + +## Constructors + +### def new`(table : Cql::Table, schema : Cql::Schema)` + +## Instance Methods + +### def add\_column`(name : Symbol, type : Any, as as_name : String | Nil = nil, null : Bool = true, default : DB::Any = nil, unique : Bool = false, size : Int32 | Nil = nil, index : Bool = false)` + +Adds a new column to the table. + +* **@param** name \[Symbol] the name of the column to be added +* **@param** type \[Any] the data type of the column +* **@param** as\_name \[String, nil] an optional alias for the column +* **@param** null \[Bool] whether the column allows null values (default: true) +* **@param** default \[DB::Any, nil] the default value for the column (default: nil) +* **@param** unique \[Bool] whether the column should have a unique constraint (default: false) +* **@param** size \[Int32, nil] the size of the column (default: nil) +* **@param** index \[Bool] whether the column should be indexed (default: false) + +**Example** Adding a new column with default options + +```crystal +add_column(:email, "string") +``` + +**Example** Adding a new column with custom options + +```crystal +add_column(:age, "integer", null: false, default: "18") +``` + +### def change\_column`(name : Symbol, type : Any)` + +Changes the type of a column in the table. + +* **@param** name \[Symbol] the name of the column to be changed +* **@param** type \[Any] the new data type for the column + +**Example** Changing the type of a column + +```crystal +change_column(:age, "string") +``` + +### def create\_index`(name : Symbol, columns : Array(Symbol), unique : Bool = false)` + +Creates an index on the table. + +* **@param** name \[Symbol] the name of the index +* **@param** columns \[Array(Symbol)] the columns to be indexed +* **@param** unique \[Bool] whether the index should be unique (default: false) + +**Example** Creating an index + +```crystal +create_index(:index_users_on_email, [:email], unique: true) +``` + +### def drop\_column`(column : Symbol)` + +Drops a column from the table. + +* **@param** column \[Symbol] the name of the column to be dropped + +**Example** Dropping a column + +```crystal +drop_column(:age) +``` + +### def drop\_foreign\_key`(name : Symbol)` + +Drops a foreign key from the table. + +* **@param** name \[Symbol] the name of the foreign key to be dropped + +**Example** Dropping a foreign key + +```crystal +drop_foreign_key(:fk_user_id) +``` + +### def drop\_index`(name : Symbol)` + +Drops an index from the table. + +* **@param** name \[Symbol] the name of the index to be dropped + +**Example** Dropping an index + +```crystal +drop_index(:index_users_on_email) +``` + +### def foreign\_key`(name : Symbol, columns : Array(Symbol), table : Symbol, references : Array(Symbol), on_delete : String = "NO ACTION", on_update : String = "NO ACTION")` + +Adds a foreign key to the table. + +* **@param** name \[Symbol] the name of the foreign key +* **@param** columns \[Array(Symbol)] the columns in the current table +* **@param** table \[Symbol] the referenced table +* **@param** references \[Array(Symbol)] the columns in the referenced table +* **@param** on\_delete \[String] the action on delete (default: "NO ACTION") +* **@param** on\_update \[String] the action on update (default: "NO ACTION") + +**Example** Adding a foreign key + +```crystal +foreign_key(:fk_user_id, [:user_id], :users, [:id], on_delete: "CASCADE") +``` + +### def rename\_column`(old_name : Symbol, new_name : Symbol)` + +Renames a column in the table. + +* **@param** old\_name \[Symbol] the current name of the column +* **@param** new\_name \[Symbol] the new name for the column + +**Example** Renaming a column + +```crystal + rename_column(:email, :user_email) +``` + +### def rename\_table`(new_name : Symbol)` + +Renames the table. + +* **@param** new\_name \[Symbol] the new name for the table + +**Example** Renaming the table + +```crystal +rename_table(:new_table_name) +``` + +### def to\_sql`(visitor : Expression::Visitor)` + +Converts the alter table actions to SQL. + +* **@param** visitor \[Expression::Visitor] the visitor to generate SQL +* **@return** \[String] the generated SQL + +**Example** Generating SQL for alter table actions + +```crystal +sql = to_sql(visitor) +``` diff --git a/docs/cql-api/class-cql-schema/class-cql-column-t.md b/docs/cql-api/class-cql-schema/class-cql-column-t.md new file mode 100644 index 0000000..682b26a --- /dev/null +++ b/docs/cql-api/class-cql-schema/class-cql-column-t.md @@ -0,0 +1,67 @@ +# class Cql::Column(T) + +`Cql::BaseColumn` < `Reference` < `Object` + +A column in a table This class represents a column in a table It provides methods for setting the column type, default value, and constraints It also provides methods for building expressions + +**Example** Creating a new column + +```crystal +schema.define do + table :users do + column :name, String, null: false, default: "John" + column :age, Int32, null: false + end +end +``` + +## Constructors + +### def new`(name : Symbol, type : T.class, as_name : String | Nil = nil, null : Bool = false, default : DB::Any = nil, unique : Bool = false, size : Int32 | Nil = nil, index : Index | Nil = nil)` + +Create a new column instance + +* **@param** : name (Symbol) - The name of the column +* **@param** : type (Any) - The data type of the column +* **@param** : as\_name (String, nil) - An optional alias for the column +* **@param** : null (Bool) - Whether the column allows null values (default: false) +* **@param** : default (DB::Any) - The default value for the column (default: nil) +* **@param** : unique (Bool) - Whether the column should have a unique constraint (default: false) +* **@param** : size (Int32, nil) - The size of the column (default: nil) +* **@param** : index (Index, nil) - The index for the column (default: nil) +* **@return** : Nil +* **@raise** : Cql::Error if the column type is not valid + +**Example** + +```crystal +column = Cql::Column.new(:name, String) +``` + +## Instance Methods + +### def expression + +Expressions for this column + +* **@return** \[Expression::ColumnBuilder] the column expression builder + +**Example** + +```crystal +column = Cql::Column.new(:name, String) +column.expression.eq("John") +``` + +### def validate!`(value)` + +Validate the value + +* **@param** value \[DB::Any] The value to validate + +**Example** + +```crystal +column = Cql::Column.new(:name, String) +column.validate!("John") +``` diff --git a/docs/cql-api/class-cql-schema/class-cql-foreignkey.md b/docs/cql-api/class-cql-schema/class-cql-foreignkey.md new file mode 100644 index 0000000..af69b46 --- /dev/null +++ b/docs/cql-api/class-cql-schema/class-cql-foreignkey.md @@ -0,0 +1,26 @@ +# class Cql::ForeignKey + +`Reference` < `Object` + +A foreign key constraint This class represents a foreign key constraint It provides methods for setting the columns, table, and references It also provides methods for setting the on delete and on update actions + +**Example** Creating a new foreign key + +```crystal +schema.build do + table :users do + column :id, Int32, primary: true + column :name, String + end +end + +table :posts do + column :id, Int32, primary: true + column :user_id, Int32 + foreign_key [:user_id], :users, [:id] +end +``` + +## Constructors + +### def new`(name : Symbol, columns : Array(Symbol), table : Symbol, references : Array(Symbol), on_delete : String = "NO ACTION", on_update : String = "NO ACTION")` diff --git a/docs/cql-api/class-cql-schema/class-cql-index.md b/docs/cql-api/class-cql-schema/class-cql-index.md new file mode 100644 index 0000000..922d790 --- /dev/null +++ b/docs/cql-api/class-cql-schema/class-cql-index.md @@ -0,0 +1,62 @@ +# class Cql::Index + +`Reference` < `Object` + +An index on a table This class represents an index on a table It provides methods for setting the columns and unique constraint It also provides methods for generating the index name + +**Example** Creating a new index + +```crystal +schema.define do + table :users do + column :name, String + column :email, String + index [:name, :email], unique: true + end +end +``` + +## Constructors + +### def new`(table : Table, columns : Array(Symbol), unique : Bool = false, name : String | Nil = nil)` + +Create a new index instance on a table + +* **@param** : table (Table) - The table to create the index on +* **@param** : columns (Array(Symbol)) - The columns to index +* **@param** : unique (Bool) - Whether the index should be unique (default: false) +* **@param** : name (String, nil) - The name of the index (default: nil) +* **@return** : Nil +* **@raise** : Cql::Error if the table does not exist +* **@raise** : Cql::Error if the column does not exist + +**Example** + +```crystal +index = Cql::Index.new(table, [:name, :email], unique: true) +``` + +## Instance Methods + +### def columns + +### def index\_name + +Generate the index name + +* **@return** : String +* **@raise** : Nil + +**Example** + +```crystal +index_name = index.index_name +``` + +### def name + +### def name=`(name : String | Nil)` + +### def table + +### def unique? diff --git a/docs/cql-api/class-cql-schema/class-cql-primarykey-t.md b/docs/cql-api/class-cql-schema/class-cql-primarykey-t.md new file mode 100644 index 0000000..7abe8a5 --- /dev/null +++ b/docs/cql-api/class-cql-schema/class-cql-primarykey-t.md @@ -0,0 +1,28 @@ +# class Cql::PrimaryKey(T) + +`Cql::Column` < `Cql::BaseColumn` < `Reference` < `Object` + +Primary key column definition + +**Example:** + +```crystal +schema.table :users do + primary :id, Int32 + column :name, String +end +``` + +## Constructors + +### def new`(name : Symbol = :id, type : PrimaryKeyType = Int64.class, as_name : String | Nil = nil, auto_increment : Bool = true, unique : Bool = true, default : DB::Any = nil)` + +## Instance Methods + +### def as\_name + +### def auto\_increment? + +### def unique? + +:nodoc: diff --git a/docs/cql-api/class-cql-schema/class-cql-table.md b/docs/cql-api/class-cql-schema/class-cql-table.md new file mode 100644 index 0000000..4774d51 --- /dev/null +++ b/docs/cql-api/class-cql-schema/class-cql-table.md @@ -0,0 +1,423 @@ +# class Cql::Table + +`Reference` < `Object` + +Represents a table in the database. This class is responsible for handling table creation, modification, and deletion. + +## Usage + +```crystal +table = Table.new(:users, schema) +=> # +``` + +```crystal +table.column(:id, Int64, primary: true) +table.column(:name, String) +table.create_sql +=> "CREATE TABLE users (id BIGINT PRIMARY KEY, name TEXT);" +``` + +```crystal +table = Table.new(:users, schema) +table.drop! +=> nil +``` + +```crystal +table = Table.new(:users, schema) +table.truncate! +=> nil + +table = Table.new(:users, schema) +table.column(:id, Int64, primary: true) +table.column(:name, String) +table.create! +=> nil +``` + +## Constants + +### Log + +```crystal +::Log.for(self) +``` + +## Constructors + +### def new`(table_name : Symbol, schema : Schema, as_name : String | Nil = nil)` + +## Instance Methods + +### def add\_index`(columns : Array(Symbol), unique : Bool = false, table : Table = self)` + +Adds a new column to the table. + +* **@param** columns \[Array(Symbol)] the columns to be indexed +* **@param** unique \[Bool] whether the index should be unique (default: false) +* **@param** table \[Table] the table to add the index to (default: self) +* **@return** \[Index] the new index + +**Example** Adding a new index + +```crystal +add_index([:email], unique: true) +add_index([:email, :username], unique: true) +add_index([:email, :username], unique: true, table: users) +``` + +### def as\_name + +### def bigint`(name : Symbol, as as_name : String | Nil = nil, null : Bool = false, default : DB::Any = nil, unique : Bool = false, index : Bool = false)` + +Adds a new column to the table. + +* **@param** name \[Symbol] the name of the column to be added +* **@param** as\_name \[String, nil] an optional alias for the column +* **@param** null \[Bool] whether the column allows null values (default: false) +* **@param** default \[DB::Any, nil] the default value for the column (default: nil) +* **@param** unique \[Bool] whether the column should have a unique constraint (default: false) +* **@param** index \[Bool] whether the column should be indexed (default: false) +* **@return** \[Column] the new column + +**Example** Adding a new column with default options + +```crystal +bigint :age +bigint :age, as: "user_age", null: false, default: 18, unique: true, index: true +``` + +### def blob`(name : Symbol, as as_name : String | Nil = nil, null : Bool = false, default : DB::Any = nil, unique : Bool = false, size : Int32 | Nil = nil, index : Bool = false)` + +Adds a new column to the table. + +* **@param** name \[Symbol] the name of the column to be added +* **@param** as\_name \[String, nil] an optional alias for the column +* **@param** null \[Bool] whether the column allows null values (default: false) +* **@param** default \[DB::Any, nil] the default value for the column (default: nil) +* **@param** unique \[Bool] whether the column should have a unique constraint (default: false) +* **@param** index \[Bool] whether the column should be indexed (default: false) +* **@return** \[Column] the new column + +**Example** Adding a new column with default options + +```crystal +blob :data +blob :data, as: "binary_data", null: false, default: nil, unique: true, index: true +``` + +### def boolean`(name : Symbol, as as_name : String | Nil = nil, null : Bool = false, default : DB::Any = nil, unique : Bool = false, index : Bool = false)` + +Adds a new column to the table. + +* **@param** name \[Symbol] the name of the column to be added +* **@param** as\_name \[String, nil] an optional alias for the column +* **@param** null \[Bool] whether the column allows null values (default: false) +* **@param** default \[DB::Any, nil] the default value for the column (default: nil) +* **@param** unique \[Bool] whether the column should have a unique constraint (default: false) +* **@param** index \[Bool] whether the column should be indexed (default: false) +* **@return** \[Column] the new column + +**Example** Adding a new column with default options + +```crystal +boolean :active +boolean :active, as: "is_active", null: false, default: false, unique: true, index: true +``` + +### def column`(name : Symbol, type : T.class, as as_name : String | Nil = nil, null : Bool = false, default : DB::Any = nil, unique : Bool = false, size : Int32 | Nil = nil, index : Bool = false) forall T` + +Adds a new column to the table. + +* **@param** name \[Symbol] the name of the column to be added +* **@param** type \[T.class] the data type of the column +* **@param** as\_name \[String, nil] an optional alias for the column +* **@param** null \[Bool] whether the column allows null values (default: false) +* **@param** default \[DB::Any, nil] the default value for the column (default: nil) +* **@param** unique \[Bool] whether the column should have a unique constraint (default: false) +* **@param** size \[Int32, nil] the size of the column (default: nil) +* **@param** index \[Bool] whether the column should be indexed (default: false) +* **@return** \[Column] the new column + +**Example** Adding a new column with default options + +```crystal +column :email, String +``` + +### def columns + +### def create! + +Creates the table in the database. + +* **@return** \[Nil] + +**Example** + +```crystal +table = Table.new(:users, schema) +table.column(:id, Int64, primary: true) +table.column(:name, String) +table.create! +=> nil +``` + +### def create\_sql + +Generates the SQL to create the table. + +**Example** + +```crystal +table = Table.new(:users, schema) +table.column(:id, Int64, primary: true) +table.column(:name, String) +table.create_sql +``` + +```crystal +=> "CREATE TABLE users (id BIGINT PRIMARY KEY, name TEXT);" +``` + +### def date`(name : Symbol, as as_name : String | Nil = nil, null : Bool = false, default : DB::Any = nil, unique : Bool = false, index : Bool = false)` + +Adds a new column to the table. + +* **@param** name \[Symbol] the name of the column to be added +* **@param** as\_name \[String, nil] an optional alias for the column +* **@param** null \[Bool] whether the column allows null values (default: false) +* **@param** default \[DB::Any, nil] the default value for the column (default: nil) +* **@param** unique \[Bool] whether the column should have a unique constraint (default: false) +* **@param** index \[Bool] whether the column should be indexed (default: false) +* **@return** \[Column] the new column + +**Example** Adding a new column with default options + +```crystal +date :birthday +date :birthday, as: "date_of_birth", null: false, default: Time.local, unique: true, index: true +``` + +### def double`(name : Symbol, as as_name : String | Nil = nil, null : Bool = false, default : DB::Any = nil, unique : Bool = false, index : Bool = false)` + +Adds a new column to the table. + +* **@param** name \[Symbol] the name of the column to be added +* **@param** as\_name \[String, nil] an optional alias for the column +* **@param** null \[Bool] whether the column allows null values (default: false) +* **@param** default \[DB::Any, nil] the default value for the column (default: nil) +* **@param** unique \[Bool] whether the column should have a unique constraint (default: false) +* **@param** index \[Bool] whether the column should be indexed (default: false) +* **@return** \[Column] the new column + +**Example** Adding a new column with default options + +```crystal +double :age +double :age, as: "user_age", null: false, default: 18.0, unique: true, index: true +``` + +### def drop! + +Drops the table from the database. + +* **@return** \[Nil] + +**Example** + +```crystal +table = Table.new(:users, schema) +table.drop! +=> nil +``` + +### def drop\_sql + +Generates the SQL to drop the table. + +* **@return** \[String] the SQL query + +**Example** + +```crystal +table = Table.new(:users, schema) +table.drop_sql +``` + +```crystal +=> "DROP TABLE users;" +``` + +### def expression + +Gets table expression for Sql query generation + +* **@return** \[Expression::Table] the table expression + +**Example** + +```crystal +table = Table.new(:users, schema) +table.expression +=> # +``` + +### def float`(name : Symbol, as as_name : String | Nil = nil, null : Bool = false, default : DB::Any = nil, unique : Bool = false, index : Bool = false)` + +Adds a new column to the table. + +* **@param** name \[Symbol] the name of the column to be added +* **@param** as\_name \[String, nil] an optional alias for the column +* **@param** null \[Bool] whether the column allows null values (default: false) +* **@param** default \[DB::Any, nil] the default value for the column (default: nil) +* **@param** unique \[Bool] whether the column should have a unique constraint (default: false) +* **@param** index \[Bool] whether the column should be indexed (default: false) +* **@return** \[Column] the new column + +**Example** Adding a new column with default options + +```crystal +float :age +float :age, as: "user_age", null: false, default: 18.0, unique: true, index: true +``` + +### def integer`(name : Symbol, as as_name : String | Nil = nil, null : Bool = false, default : DB::Any = nil, unique : Bool = false, index : Bool = false)` + +Adds a new column to the table. + +* **@param** name \[Symbol] the name of the column to be added +* **@param** as\_name \[String, nil] an optional alias for the column +* **@param** null \[Bool] whether the column allows null values (default: false) +* **@param** default \[DB::Any, nil] the default value for the column (default: nil) +* **@param** unique \[Bool] whether the column should have a unique constraint (default: false) +* **@param** index \[Bool] whether the column should be indexed (default: false) +* **@return** \[Column] the new column + +**Example** Adding a new column with default options + +```crystal +integer :age +integer :age, as: "user_age", null: false, default: 18, unique: true, index: true +``` + +### def interval`(name : Symbol, as as_name : String | Nil = nil, null : Bool = false, default : DB::Any = nil, unique : Bool = false, index : Bool = false)` + +Adds a new column to the table. + +* **@param** name \[Symbol] the name of the column to be added +* **@param** as\_name \[String, nil] an optional alias for the column +* **@param** null \[Bool] whether the column allows null values (default: false) +* **@param** default \[DB::Any, nil] the default value for the column (default: nil) +* **@param** unique \[Bool] whether the column should have a unique constraint (default: false) +* **@param** index \[Bool] whether the column should be indexed (default: false) +* **@return** \[Column] the new column + +**Example** Adding a new column with default options + +```crystal +interval :duration +interval :duration, as: "time_span", null: false, default: Time.local, unique: true, index: true +``` + +### def primary`(name : Symbol = :id, type : T.class = Int64, auto_increment : Bool = true, as as_name = nil, unique : Bool = true) forall T` + +Adds a new column to the table. + +* **@param** name \[Symbol] the name of the column to be added +* **@param** type \[Any] the data type of the column +* **@param** auto\_increment \[Bool] whether the column should auto increment (default: true) +* **@param** as\_name \[String, nil] an optional alias for the column +* **@param** unique \[Bool] whether the column should have a unique constraint (default: true) + +**Example** Adding a new primary key column + +```crystal +primary :id, Int64 +primary :id, Int64, auto_increment: false +``` + +### def table\_name + +### def table\_name=`(table_name : Symbol)` + +### def text`(name : Symbol, as as_name : String | Nil = nil, null : Bool = false, default : DB::Any = nil, unique : Bool = false, size : Int32 | Nil = nil, index : Bool = false)` + +Adds a new column to the table. + +* **@param** name \[Symbol] the name of the column to be added +* **@param** as\_name \[String, nil] an optional alias for the column +* **@param** null \[Bool] whether the column allows null values (default: false) +* **@param** default \[DB::Any, nil] the default value for the column (default: nil) +* **@param** unique \[Bool] whether the column should have a unique constraint (default: false) +* **@param** index \[Bool] whether the column should be indexed (default: false) +* **@return** \[Column] the new column + +**Example** Adding a new column with default options + +```crystal +decimal :price +decimal :price, as: "product_price", null: false, default: 0.0, unique: true, index: true +``` + +### def timestamp`(name : Symbol, as as_name : String | Nil = nil, null : Bool = false, default : DB::Any = nil, unique : Bool = false, index : Bool = false)` + +Adds a new column to the table. + +* **@param** name \[Symbol] the name of the column to be added +* **@param** as\_name \[String, nil] an optional alias for the column +* **@param** null \[Bool] whether the column allows null values (default: false) +* **@param** default \[DB::Any, nil] the default value for the column (default: nil) +* **@param** unique \[Bool] whether the column should have a unique constraint (default: false) +* **@param** index \[Bool] whether the column should be indexed (default: false) +* **@return** \[Column] the new column + +**Example** Adding a new column with default options + +```crystal +timestamp :created_at +timestamp :created_at, as: "created_at", null: false, default: Time.local, unique: true, index: true +``` + +### def timestamps + +Adds a new column to the table. + +**Example** Adding timestamps to the table + +```crystal +timestamps +``` + +### def truncate! + +Truncates the table in the database. + +* **@return** \[Nil] + +**Example** + +```crystal +table = Table.new(:users, schema) +table.truncate! +=> nil +``` + +### def truncate\_sql + +Generates the SQL to truncate the table. + +* **@return** \[String] the SQL query + +**Example** + +```crystal +table = Table.new(:users, schema) +table.truncate_sql +=> "TRUNCATE TABLE users;" +``` + +## Macros + +### macro method\_missing`(call)` diff --git a/docs/cql-api/class-cql-schema/enum-cql-adapter.md b/docs/cql-api/class-cql-schema/enum-cql-adapter.md new file mode 100644 index 0000000..f60101d --- /dev/null +++ b/docs/cql-api/class-cql-schema/enum-cql-adapter.md @@ -0,0 +1,41 @@ +# enum Cql::Adapter + +`Enum` < `Comparable` < `Value` < `Object` + +Represents a database adapter module. + +## Constants + +### Sqlite + +```crystal +0 +``` + +### MySql + +```crystal +1 +``` + +### Postgres + +```crystal +2 +``` + +## Instance Methods + +### def my\_sql? + +### def postgres? + +### def sql\_type`(type) : String` + +Returns the SQL type for the given type. @param type \[Type] the type @return \[String] the SQL type **Example** Getting the SQL type + +```crystal +Cql::Adapter::Sqlite.sql_type(Int32) # => "INTEGER" +``` + +### def sqlite? diff --git a/docs/cql-api/class-cql-schema/module-cql-record-t-pk.md b/docs/cql-api/class-cql-schema/module-cql-record-t-pk.md new file mode 100644 index 0000000..630e6a5 --- /dev/null +++ b/docs/cql-api/class-cql-schema/module-cql-record-t-pk.md @@ -0,0 +1,190 @@ +# module Cql::Record(T, Pk) + +Write documentation for Record module + +**Example** Using the Record module + +```crystal +AcmeDB = Cql::Schema.define(:acme_db, adapter: Cql::Adapter::Postgres, + uri: "postgresql://example:example@localhost:5432/example") do + table :posts do + primary :id, Int64, auto_increment: true + text :title + text :body + timestamp :published_at + end + + table :comments do + primary + bigint :post_id + text :body + end +end + +struct Post + include Cql::Record(Post) + + define AcmeDB, :posts + + getter id : Int64? + getter title : String + getter body : String + getter published_at : Time + + def initialize(@title : String, @body : String, @published_at : Time = Time.utc) + end +end + +struct Comment + include Cql::Record(Comment) + define AcmeDB, :comments + + getter id : Int64? + getter post_id : Int64 + getter body : String + + def initialize(@post_id : Int64, @body : String) + end +end +``` + +## Instance Methods + +### def attributes`(attrs : Hash(Symbol, DB::Any))` + +Set the record's attributes from a hash + +* **@param** attrs \[Hash(Symbol, DB::Any)] The attributes to set +* **@return** \[Nil] + +**Example** Setting the record's attributes + +```crystal +user.attributes = {name: "Alice", email: "[email protected]"} +``` + +### def attributes + +Define instance-level methods for querying and manipulating data Fetch the record's ID or raise an error if it's nil + +* **@return** \[PrimaryKey] The ID + +**Example** Fetching the record's ID + +```crystal +user.attributes +-> { id: 1, name: "Alice", email: " [email protected]" } +``` + +### def delete + +Delete the record from the database + +* **@return** \[Nil] + +**Example** Deleting the record + +```crystal +user.delete +``` + +### def id + +Identity method for the record ID + +* **@return** \[PrimaryKey] The ID + +**Example** Fetching the record's ID + +```crystal +user.id +``` + +### def id=`(id : Pk)` + +Set the record's ID + +* **@param** id \[PrimaryKey] The ID + +**Example** Setting the record's ID + +```crystal +user.id = 1 +``` + +### def persisted? + +Check if the record has been persisted to the database + +* **@return** \[Bool] True if the record has an ID, false otherwise + +**Example** Checking if the record is persisted + +```crystal +user.persisted? +``` + +### def reload! + +Define instance-level methods for querying and manipulating data Fetch the record's ID or raise an error if it's nil + +* **@return** \[PrimaryKey] The ID + +**Example** Fetching the record's ID + +```crystal +user.reload! +``` + +### def save + +Define instance-level methods for saving and deleting records Save the record to the database or update it if it already exists + +* **@return** \[Nil] + +**Example** Saving the record + +```crystal +user.save +``` + +### def update`(fields : Hash(Symbol, DB::Any))` + +Delete the record from the database if it exists + +* **@return** \[Nil] + +**Example** Deleting the record + +```crystal +user.delete +``` + +### def update + +Update the record with the given record object + +**Example** Updating the record + +```crystal +bob = User.new(name: "Bob", email: " [email protected]") +id = bob.save + +bob.reload! +bon.name = "Juan" + +bob.update +``` + +### def update + +Update the record with the given fields + +* **@param** fields \[Hash(Symbol, DB::Any)] The fields to update +* **@return** \[Nil] + +**Example** Updating the record + +```crystal +user.update(name: "Alice", email: " [email protected]") +``` diff --git a/docs/cql-api/crud/README.md b/docs/cql-api/crud/README.md new file mode 100644 index 0000000..7baeaf3 --- /dev/null +++ b/docs/cql-api/crud/README.md @@ -0,0 +1,2 @@ +# CRUD + diff --git a/docs/cql-api/crud/class-cql-delete.md b/docs/cql-api/crud/class-cql-delete.md new file mode 100644 index 0000000..783d929 --- /dev/null +++ b/docs/cql-api/crud/class-cql-delete.md @@ -0,0 +1,173 @@ +# class Cql::Delete + +`Reference` < `Object` + +A delete query This class represents a delete query It provides methods for building a delete query It also provides methods for executing the query + +**Example** Deleting a record + +```crystal +delete.from(:users).where(id: 1).commit +``` + +## Constructors + +### def new`(schema : Schema)` + +Initialize the delete query + +* **@param** schema \[Schema] The schema to use +* **@return** \[Delete] The delete query object + +**Example** Deleting a record + +```crystal +delete = Cql::Delete.new(schema) + .from(:users) + .where(id: 1) + .commit +``` + +## Instance Methods + +### def back`(*columns : Symbol)` + +Sets the columns to return after the delete + +* **@param** columns \[Symbol\*] The columns to return +* **@return** \[self] The current instance +* **@raise** \[Exception] If the column does not exist + +**Example** Setting the columns to return + +```crystal +delete = Cql::Delete.new(schema) + .from(:users) + .back(:name, :age) +``` + +### def build + +Builds the delete expression + +* **@return** \[Expression::Delete] The delete expression +* **@raise** \[Exception] If the table is not set +* **@raise** \[Exception] If the where clause is not set + +**Example** Building the delete expression + +```crystal +delete = Cql::Delete.new(schema) + .from(:users) + .where(id: 1) + .commit +``` + +### def commit + +Executes the delete query and returns the result + +* **@return** \[DB::Result] The result of the query + +**Example** Deleting a record + +```crystal +delete = Cql::Delete.new(schema) + .from(:users) + .where(id: 1) + .commit +``` + +### def from`(table : Symbol)` + +Sets the table to delete from + +* **@param** table \[Symbol] The name of the table +* **@return** \[self] The current instance +* **@raise** \[Exception] If the table does not exist + +**Example** Setting the table + +```crystal +delete = Cql::Delete.new(schema) + .from(:users) +``` + +### def to\_sql`(gen = @schema.gen)` + +Generates the SQL query and parameters + +* **@param** gen \[Expression::Generator] The generator to use +* **@return** \[{String, Array(DB::Any)}] The query and parameters + +**Example** Generating a delete query + +```crystal +delete = Cql::Delete.new(schema) + .from(:users) + .where(id: 1) + .to_sql +``` + +### def using`(table : Symbol)` + +Sets the table to use in the using clause + +* **@param** table \[Symbol] The name of the table +* **@return** \[self] The current instance +* **@raise** \[Exception] If the table does not exist + +**Example** Setting the using table + +```crystal +delete = Cql::Delete.new(schema) + .from(:users) + .using(:posts) +``` + +### def where + +Sets the columns to return + +* **@param** columns \[Symbol\*] The columns to return +* **@return** \[self] The current instance +* **@raise** \[Exception] If the column does not exist + +**Example** Setting the columns to return + +```crystal +delete = Cql::Delete.new(schema) + .from(:users) + .back(:name, :age) +``` + +### def where`(attr : Hash(Symbol, DB::Any))` + +Where clause using a hash of conditions to match against + +* **@param** attr \[Hash(Symbol, DB::Any)] The conditions to match against +* **@return** \[self] The current instance + +**Example** Setting the where clause + +```crystal +delete = Cql::Delete.new(schema) + .from(:users) + .where(id: 1) +``` + +### def where + +Sets the columns to return + +* **@param** columns \[Symbol\*] The columns to return +* **@return** \[self] The current instance +* **@raise** \[Exception] If the column does not exist + +**Example** Setting the columns to return + +```crystal +delete = Cql::Delete.new(schema) + .from(:users) + .back(:name, :age) +``` diff --git a/docs/cql-api/crud/class-cql-insert.md b/docs/cql-api/crud/class-cql-insert.md new file mode 100644 index 0000000..f41d584 --- /dev/null +++ b/docs/cql-api/crud/class-cql-insert.md @@ -0,0 +1,190 @@ +# class Cql::Insert + +`Reference` < `Object` + +An insert statement builder class This class provides methods for building an insert statement It also provides methods for executing the statement + +**Example** Inserting a record + +```crystal +insert + .into(:users) + .values(name: "John", age: 30) + .last_insert_id +``` + +**Example** Inserting multiple records + +```crystal +insert + .into(:users) + .values( + [ + {name: "John", age: 30}, + {name: "Jane", age: 25}, + ] + ).commit +``` + +**Example** Inserting a record with a query + +```crystal +insert + .into(:users) + .query( + select.from(:users).where(id: 1) + ).commit +``` + +## Constants + +### Log + +```crystal +::Log.for(self) +``` + +## Constructors + +### def new`(schema : Schema)` + +## Instance Methods + +### def back`(*columns : Symbol)` + +Set the columns to return + +* **@param** columns \[Symbol\*] The columns to return +* **@return** \[Insert] The insert object +* **@raise** \[Exception] If the column does not exist + +**Example** Inserting a record + +```crystal +insert.into(:users).values(name: "John", age: 30).back(:id).commit +``` + +### def build + +Build the insert statement object **@return** \[Expression::Insert] The insert statement + +**Example** Building the insert statement + +```crystal +insert.into(:users).values(name: "John", age: 30).commit +``` + +### def commit + +Executes the insert statement and returns the result + +* **@return** \[Int64] The last inserted ID + +**Example** Inserting a record + +```crystal +insert + .into(:users) + .values(name: "John", age: 30) + .commit + +=> 1 +``` + +### def into`(table : Symbol)` + +Set the table to insert into + +* **@param** table \[Symbol] The table to insert into +* **@return** \[Insert] The insert object + +**Example** Inserting a record + +```crystal +insert + .into(:users) + .values(name: "John", age: 30) + .commit +``` + +### def last\_insert\_id`(as type : PrimaryKeyType = Int64)` + +Inserts and gets the last inserted ID from the database Works with SQLite, PostgreSQL and MySQL. + +* **@return** \[Int64] The last inserted ID + +**Example** Getting the last inserted ID + +```crystal +insert.into(:users).values(name: "John", age: 30).last_insert_id +``` + +### def query`(query : Query)` + +Set the query to use for the insert + +* **@param** query \[Query] The query to use +* **@return** \[Insert] The insert object + +**Example** Inserting a record with a query + +```crystal +insert.into(:users).query(select.from(:users).where(id: 1)).commit +``` + +### def to\_sql`(gen = @schema.gen)` + +Convert the insert object to a SQL query + +* **@param** gen \[Generator] The generator to use +* **@return** \[{String, Array(DB::Any)}] The query and parameters +* **@raise** \[Exception] If the table does not exist + +**Example** Generating a SQL query + +```crystal +insert.into(:users).values(name: "John", age: 30).to_sql +``` + +### def values`(values : Array(Hash(Symbol, DB::Any)))` + +Set the columns to insert + +* **@param** columns \[Array(Symbol)] The columns to insert +* **@return** \[Insert] The insert object + +**Example** Inserting a record + +```crystal +insert + .into(:users) + .columns(:name, :age) + .values("John", 30) + .commit +``` + +### def values`(hash : Hash(Symbol, DB::Any))` + +Set the values to insert + +* **@param** hash \[Hash(Symbol, DB::Any)] The values to insert +* **@return** \[Insert] The insert object + +**Example** Inserting a record + +```crystal +insert.into(:users).values(name: "John", age: 30).commit +``` + +### def values + +Set the values to insert + +* **@param** fields \[Hash(Symbol, DB::Any)] The values to insert +* **@return** \[Insert] The insert object + +**Example** Inserting a record + +```crystal +insert.into(:users).values(name: "John", age: 30).commit +``` diff --git a/docs/cql-api/crud/class-cql-query.md b/docs/cql-api/crud/class-cql-query.md new file mode 100644 index 0000000..548f0fb --- /dev/null +++ b/docs/cql-api/crud/class-cql-query.md @@ -0,0 +1,547 @@ +# class Cql::Query + +`Reference` < `Object` + +The `Query` class is responsible for building SQL queries in a structured manner. It holds various components like selected columns, tables, conditions, and more. It provides methods to execute the query and return results. + +**Example** Creating a new query + +```crystal +schema = Cql::Schema.new + +Cql::Query.new(schema) +query.select(:name, :age).from(:users).where(name: "John").all(User) +=> [{"name" => "John", "age" => 30}] +``` + +**Example** Executing a query and iterating over results + +```crystal +schema = Cql::Schema.new +query = Cql::Query.new(schema) +query.select(:name, :age).from(:users).where(name: "John").each(User) do |user| + puts user.name +end + +=> John +``` + +## Constructors + +### def new`(schema : Schema)` + +Initializes the `Query` object with the provided schema. + +* **@param** schema \[Schema] The schema object to use for the query +* **@return** \[Query] The query object + +**Example** Creating a new query + +```crystal +schema = Cql::Schema.new +query = Cql::Query.new(schema) + +=> # +``` + +## Instance Methods + +### def aggr\_columns + +### def all`(as as_kind)` + +Executes the query and returns all records. + +* **@param** as \[Type] The type to cast the results to +* **@return** \[Array(Type)] The results of the query + +**Example** + +```crystal +schema = Cql::Schema.new +query = Cql::Query.new(schema) +query.select(:name, :age).from(:users).all(User) + +=> [, ] +``` + +### def all!`(as as_kind)` + +* **@param** as \[Type] The type to cast the results to +* **@return** \[Array(Type)] The results of the query + +**Example** + +```crystal +schema = Cql::Schema.new +query = Cql::Query.new(schema) +query.select(:name, :age).from(:users).all!(User) + +=> [, ] +``` + +### def avg`(column : Symbol)` + +Adds an AVG aggregate function to the query. + +* **@param** column \[Symbol] The column to average +* **@return** \[Query] The query object + +**Example** + +```crystal +query.avg(:rating) +=> "SELECT AVG(rating) FROM users" +``` + +### def build + +Builds the final query expression. + +* **@return** \[Expression::Query] The query expression + +**Example** + +```crystal +query.build +=> # +``` + +### def columns + +### def count`(column : Symbol = :*)` + +Adds a COUNT aggregate function to the query. + +* **@param** column \[Symbol] The column to count +* **@return** \[Query] The query object + +**Example** + +```crystal +query.count(:id) +=> "SELECT COUNT(id) FROM users" +``` + +### def distinct + +Sets the distinct flag to true. + +* **@return** \[Query] The query object + +**Example** + +```crystal +query.from(:users).distinct +=> "SELECT DISTINCT * FROM users" +``` + +### def distinct? + +### def each`(as as_kind, &)` + +Iterates over each result and yields it to the provided block. Example: + +```crystal +query.each(User) do |user| + puts user.name +end + +=> John +``` + +### def first`(as as_kind)` + +Executes the query and returns the first record. + +* **@param** as \[Type] The type to cast the result to +* **@return** \[Type] The first result of the query + +**Example** + +```crystal +schema = Cql::Schema.new +query = Cql::Query.new(schema) +query.select(:name, :age).from(:users).first(User) + +=> +``` + +### def first!`(as as_kind)` + +* **@param** as \[Type] The type to cast the result to +* **@return** \[Type] The first result of the query + +**Example** + +```crystal +schema = Cql::Schema.new +query = Cql::Query.new(schema) +query.select(:name, :age).from(:users).first!(User) + +=> +``` + +### def from`(*tbls : Symbol)` + +Specifies the tables to select from. + +* **@param** tbls \[Symbol\*] The tables to select from +* **@return** \[Query] The query object + +**Example** + +```crystal +query.from(:users, :orders) +=> "SELECT * FROM users, orders" +``` + +### def get`(as as_kind)` + +Executes the query and returns a scalar value. + +* **@param** as \[Type] The type to cast the result to +* **@return** \[Type] The scalar result of the query Example: `query.get(Int64)` + +```crystal +schema = Cql::Schema.new +query = Cql::Query.new(schema) +query.select(:count).from(:users).get(Int64) + +=> 10 +``` + +### def group`(*columns)` + +Specifies the columns to group by. + +* **@param** columns \[Symbol\*] The columns to group by +* **@return** \[Query] The query object + +**Example** + +```crystal +query.from(:products).group(:category) +=> "SELECT * FROM products GROUP BY category" +``` + +### def group\_by + +### def having + +### def having + +Adds a HAVING condition to the grouped results. + +* **@param** block \[Block] The block to evaluate the having condition +* **@return** \[Query] The query object + +**Example** + +```crystal +query.from(:products).group(:category).having { avg(:price) > 100 } +=> "SELECT * FROM products GROUP BY category HAVING AVG(price) > 100" +``` + +### def inner`(table : Symbol, on : Hash(Cql::BaseColumn, Cql::BaseColumn | DB::Any))` + +Adds an INNER JOIN to the query. + +* **@param** table \[Symbol] The table to join +* **@param** on \[Hash(Cql::BaseColumn, Cql::BaseColumn | DB::Any)] The join condition +* **@return** \[Query] The query object + +**Example** + +```crystal +query.inner(:orders, on: { users.id => orders.user_id }) +=> "SELECT * FROM users INNER JOIN orders ON users.id = orders.user_id" +``` + +### def inner`(table : Symbol, &)` + +Adds an INNER JOIN to the query. + +* **@param** table \[Symbol] The table to join +* **@yield** \[FilterBuilder] The block to build the conditions +* **@return** \[Query] The query object +* **@raise** \[Exception] if the block is not provided +* **@raise** \[Exception] if the block does not return an expression +* **@raise** \[Exception] if the column does not exist + +**Example** + +```crystal +query.inner(:orders) { |w| w.users.id == orders.user_id } +=> "SELECT * FROM users INNER JOIN orders ON users.id = orders.user_id" +``` + +### def joins + +### def left`(table : Symbol, on : Hash(Cql::BaseColumn, Cql::BaseColumn | DB::Any))` + +Adds a LEFT JOIN to the query. + +* **@param** table \[Symbol] The table to join +* **@param** on \[Hash(Cql::BaseColumn, Cql::BaseColumn | DB::Any)] The join condition + +**Example** + +```crystal +query.left(:orders, on: { users.id => orders.user_id }) +=> "SELECT * FROM users LEFT JOIN orders ON users.id = orders.user_id" +``` + +### def left`(table : Symbol, &)` + +Adds a LEFT JOIN to the query using a block. + +* **@param** table \[Symbol] The table to join +* **@yield** \[FilterBuilder] The block to build the conditions +* **@return** \[Query] The query object +* **@raise** \[Exception] if the block is not provided +* **@raise** \[Exception] if the block does not return an expression +* **@raise** \[Exception] if the column does not exist + +**Example** + +```crystal +query.left(:orders) { |w| w.users.id == orders.user_id } +=> "SELECT * FROM users LEFT JOIN orders ON users.id = orders.user_id" +``` + +### def limit`(value : Int32)` + +Sets the limit for the number of records to return. + +* **@param** value \[Int32] The limit value +* **@return** \[Query] The query object + +**Example** + +```crystal +query.from(:users).limit(10) +=> "SELECT * FROM users LIMIT 10" +``` + +### def limit + +### def max`(column : Symbol)` + +Adds a MAX aggregate function to the query. + +* **@param** column \[Symbol] The column to find the maximum value of +* **@return** \[Query] The query object + +**Example** + +```crystal +query.from(:users).max(:price) +=> "SELECT MAX(price) FROM users" +``` + +### def min`(column : Symbol)` + +Adds a MIN aggregate function to the query. + +* **@param** column \[Symbol] The column to find the minimum value of +* **@return** \[Query] The query object + +**Example** + +```crystal +query.min(:price) +=> "SELECT MIN(price) FROM users" +``` + +### def offset`(value : Int32)` + +Sets the offset for the query. + +* **@param** value \[Int32] The offset value +* **@return** \[Query] The query object + +**Example** + +```crystal +query.from(:users).limit(10).offset(20) +=> "SELECT * FROM users LIMIT 10 OFFSET 20" +``` + +### def offset + +### def order`(*fields)` + +Specifies the columns to order by. + +* **@param** fields \[Symbol\*] The columns to order by +* **@return** \[Query] The query object + +**Example** + +```crystal +query.order(:name, :age) +=> "SELECT * FROM users ORDER BY name, age" +``` + +### def order + +Specifies the columns to order by. + +* **@param** fields \[Hash(Symbol, Symbol)] The columns to order by and their direction +* **@return** \[Query] The query object + +**Example** + +```crystal +query.order(name: :asc, age: :desc) +=> "SELECT * FROM users ORDER BY name ASC, age DESC" +``` + +### def order\_by + +### def right`(table : Symbol, on : Hash(Cql::BaseColumn, Cql::BaseColumn | DB::Any))` + +Adds a RIGHT JOIN to the query. + +* **@param** table \[Symbol] The table to join +* **@param** on \[Hash(Cql::BaseColumn, Cql::BaseColumn | DB::Any)] The join condition +* **@return** \[Query] The query object + +**Example** + +```crystal +query.right(:orders, on: { users.id => orders.user_id }) +=> "SELECT * FROM users RIGHT JOIN orders ON users.id = orders.user_id" +``` + +### def right`(table : Symbol, &)` + +Adds a RIGHT JOIN to the query using a block. + +* **@param** table \[Symbol] The table to join +* **@yield** \[FilterBuilder] The block to build the conditions +* **@return** \[Query] The query object +* **@raise** \[Exception] if the block is not provided +* **@raise** \[Exception] if the block does not return an expression +* **@raise** \[Exception] if the column does not exist +* **@raise** \[Exception] if the value is invalid + +**Example** + +```crystal +query.right(:orders) { |w| w.users.id == orders.user_id } +=> "SELECT * FROM users RIGHT JOIN orders ON users.id = orders.user_id" +``` + +### def select`(*columns : Symbol)` + +Specifies the columns to select. + +* **@param** columns \[Symbol\*] The columns to select +* **@return** \[Query] The query object + +**Example** + +```crystal +query.select(:name, :age) +=> "SELECT name, age FROM users" +``` + +### def select + +Specifies the columns to select. + +* **@param** columns \[Array(Symbol)] The columns to select +* **@return** \[Query] The query object + +**Example** + +```crystal +query.from(:users, :address).select(users: [:name, :age], address: [:city, :state]) +=> "SELECT users.name, users.age, address.city, address.state FROM users, address" +``` + +### def sum`(column : Symbol)` + +Adds a SUM aggregate function to the query. + +* **@param** column \[Symbol] The column to sum +* **@return** \[Query] The query object + +**Example** + +```crystal +query.sum(:total_amount) +=> "SELECT SUM(total_amount) FROM users" +``` + +### def tables + +### def to\_sql`(gen = @schema.gen)` + +Converts the query into an SQL string and its corresponding parameters. + +* **@param** gen \[Generator] The generator to use for converting the query +* **@return** \[Tuple(String, Array(DB::Any))] The SQL query and its parameters + +**Example** + +```crystal +query.to_sql +=> {"SELECT * FROM users WHERE name = ? AND age = ?", ["John", 30]} +``` + +### def where`(hash : Hash(Symbol, DB::Any))` + +Adds a WHERE condition with a hash of column-value pairs. + +* **@param** hash \[Hash(Symbol, DB::Any)] The hash of column-value pairs +* **@return** \[Query] The query object + +**Example** + +```crystal +query.from(:users).where(name: "John", age: 30) +=> "SELECT * FROM users WHERE name = 'John' AND age = 30" +``` + +### def where + +### def where + +Adds a WHERE condition with a block. + +* **@fields** \[FilterBuilder] The block to build the conditions +* **@return** \[Query] The query object +* **@raise** \[Exception] if the column does not exist +* **@raise** \[Exception] if the value is invalid +* **@raise** \[Exception] if the value is not of the correct type + +**Example** + +```crystal +query.from(:users).where(name: "John") +=> "SELECT * FROM users WHERE name = 'John'" +``` + +### def where + +Adds WHERE conditions using a block. + +* **@yield** \[FilterBuilder] The block to build the conditions +* **@return** \[Query] The query object +* **@raise** \[Exception] if the block is not provided +* **@raise** \[Exception] if the block does not return an expression +* **@raise** \[Exception] if the column does not exist + +**Example** + +```crystal +query.from(:users).where { |w| w.name == "John" } +=> "SELECT * FROM users WHERE name = 'John'" +``` + +## Macros + +### macro method\_missing`(call)` diff --git a/docs/cql-api/crud/class-cql-update.md b/docs/cql-api/crud/class-cql-update.md new file mode 100644 index 0000000..d424807 --- /dev/null +++ b/docs/cql-api/crud/class-cql-update.md @@ -0,0 +1,230 @@ +# class Cql::Update + +`Reference` < `Object` + +The `Cql::Update` class represents an SQL UPDATE statement. + +**Example** + +```crystal +update = Cql::Update.new(schema) + .table(:users) + .set(name: "John", age: 30) + .where { |w| w.id == 1 } + .commit +``` + +## Usage + +* `initialize(schema : Schema)` - Initializes a new instance of `Cql::Update` with the given schema. +* `commit : DB::Result` - Executes the update query and returns the result. +* `to_sql(gen = @schema.gen) : {String, Array(DB::Any)}` - Generates the SQL query and parameters. +* `table(table : Symbol) : self` - Sets the table to update. +* `set(setters : Hash(Symbol, DB::Any)) : self` - Sets the column values to update using a hash. +* `set(**fields) : self` - Sets the column values to update using keyword arguments. +* `where(&block) : self` - Sets the WHERE clause using a block. +* `where(**fields) : self` - Sets the WHERE clause using keyword arguments. +* `back(*columns : Symbol) : self` - Sets the columns to return after the update. +* `build : Expression::Update` - Builds the `Expression::Update` object. details Table of Contents \[\[toc]] + +## Constructors + +### def new`(schema : Schema)` + +## Instance Methods + +### def back`(*columns : Symbol)` + +Sets the columns to return after the update. + +* **@param** columns \[Array(Symbol)] the columns to return +* **@return** \[self] the current instance + +**Example** + +```crystal +update = Cql::Update.new(schema) + .table(:users) + .set(name: "John", age: 30) + .where { |w| w.id == 1 } + .back(:name, :age) + .commit + +=> {"UPDATE users SET name = $1, age = $2 WHERE id = $3 RETURNING name, age", ["John", 30, 1]} +``` + +### def build + +Builds the `Expression::Update` object. + +* **@return** \[Expression::Update] the update expression +* **@raise** \[Exception] if the table is not set + +**Example** + +```crystal +update = Cql::Update.new(schema) + .table(:users) + .set(name: "John", age: 30) + .where { |w| w.id == 1 } + .commit + +=> {"UPDATE users SET name = $1, age = $2 WHERE id = $3", ["John", 30, 1]} +``` + +### def commit + +Executes the update query and returns the result. + +* **@return** \[DB::Result] the result of the query + +**Example** + +```crystal +update = Cql::Update.new(schema) + .table(:users) + .set(name: "John", age: 30) + .where { |w| w.id == 1 } + .commit + +=> {"UPDATE users SET name = $1, age = $2 WHERE id = $3", ["John", 30, 1]} +``` + +### def set`(setters : Hash(Symbol, DB::Any))` + +Sets the column values to update using a hash. + +* **@param** setters \[Hash(Symbol, DB::Any)] the column values to update +* **@return** \[self] the current instance + +**Example** + +```crystal +update = Cql::Update.new(schema) + .table(:users) + .set(name: "John", age: 30) + .where { |w| w.id == 1 } + .commit + +=> {"UPDATE users SET name = $1, age = $2 WHERE id = $3", ["John", 30, 1]} +``` + +### def set + +Sets the column values to update using keyword arguments. + +* **@param** fields \[Hash(Symbol, DB::Any)] the column values to update +* **@return** \[self] the current instance + +**Example** + +```crystal +update = Cql::Update.new(schema) + .table(:users) + .set(name: "John", age: 30) + .where { |w| w.id == 1 } + .commit + +=> {"UPDATE users SET name = $1, age = $2 WHERE id = $3", ["John", 30, 1]} +``` + +### def table`(table : Symbol)` + +Sets the table to update. + +* **@param** table \[Symbol] the name of the table +* **@return** \[self] the current instance +* **@raise** \[Exception] if the table does not exist + +**Example** + +```crystal +update = Cql::Update.new(schema) + .table(:users) + .set(name: "John", age: 30) + .where { |w| w.id == 1 } + .commit + +=> {"UPDATE users SET name = $1, age = $2 WHERE id = $3", ["John", 30, 1]} +``` + +### def to\_sql`(gen = @schema.gen)` + +Generates the SQL query and parameters. + +* **@param** gen \[Expression::Generator] the generator to use +* **@return** \[{String, Array(DB::Any)}] the query and parameters + +**Example** + +```crystal +update = Cql::Update.new(schema) + .table(:users) + .set(name: "John", age: 30) + .where { |w| w.id == 1 } + .to_sql + +=> {"UPDATE users SET name = $1, age = $2 WHERE id = $3", ["John", 30, 1]} +``` + +### def where + +Sets the WHERE clause using a block. + +* **@block** w \[Expression::FilterBuilder] the filter builder +* **@return** \[self] the current instance +* **@raise** \[Exception] if the block is not provided +* **@raise** \[Exception] if the block does not return an expression + +**Example** + +```crystal +update = Cql::Update.new(schema) + .table(:users) + .set(name: "John", age: 30) + .where { |w| w.id == 1 } + .commit + +=> {"UPDATE users SET name = $1, age = $2 WHERE id = $3", ["John", 30, 1]} +``` + +### def where`(attr : Hash(Symbol, DB::Any))` + +Sets the columns to return after the update. + +* **@param** columns \[Array(Symbol)] the columns to return +* **@return** \[self] the current instance +* **@raise** \[Exception] if the column does not exist +* **@raise** \[Exception] if the column is not part of the table + +**Example** + +```crystal +update = Cql::Update.new(schema) + .table(:users) + .set(name: "John", age: 30) + .where { |w| w.id == 1 } + .back(:name, :age) + .commit +``` + +### def where + +Sets the WHERE clause using keyword arguments. + +* **@param** fields \[Hash(Symbol, DB::Any)] the conditions +* **@return** \[self] the current instance +* **@raise** \[Exception] if the column does not exist +* **@raise** \[Exception] if the value is invalid + +**Example** + +```crystal +update = Cql::Update.new(schema) + .table(:users) + .set(name: "John", age: 30) + .where(id: 1) + .commit + +=> {"UPDATE users SET name = $1, age = $2 WHERE id = $3", ["John", 30, 1]} +``` diff --git a/docs/cql-api/migrations/README.md b/docs/cql-api/migrations/README.md new file mode 100644 index 0000000..0ce35ad --- /dev/null +++ b/docs/cql-api/migrations/README.md @@ -0,0 +1,2 @@ +# Migrations + diff --git a/docs/cql-api/migrations/class-cql-migration.md b/docs/cql-api/migrations/class-cql-migration.md new file mode 100644 index 0000000..26cc003 --- /dev/null +++ b/docs/cql-api/migrations/class-cql-migration.md @@ -0,0 +1,104 @@ +# class Cql::Migration + +`Reference` < `Object` + +Migrations are used to manage changes to the database schema over time. Each migration is a subclass of `Migration` and must implement the `up` and `down` methods. + +The `up` method is used to apply the migration, while the `down` method is used to rollback the migration. Migrations are executed in their version order defined. The `Migrator` class is used to manage migrations and provides methods to apply, rollback, and redo migrations. The `Migrator` class also provides methods to list applied and pending migrations. + +**Example** Creating a new migration + +```crystal +class CreateUsersTable < Cql::Migration + self.version = 1_i64 + + def up + schema.alter :users do + add_column :name, String + add_column :age, Int32 + end + end + + def down + schema.alter :users do + drop_column :name + drop_column :age + end + end +end +``` + +**Example** Applying migrations + +```crystal +schema = Cql::Schema.define(:northwind, "sqlite3://db.sqlite3") do |s| + table :schema_migrations do + primary :id, Int32 + column :name, String + column :version, Int64, index: true, unique: true + timestamps + end +end +migrator = Cql::Migrator.new(schema) +migrator.up +``` + +**Example** Rolling back migrations + +```crystal +migrator.down +``` + +**Example** Redoing migrations + +```crystal +migrator.redo +``` + +**Example** Rolling back to a specific version + +```crystal +migrator.down_to(1_i64) +``` + +**Example** Applying to a specific version + +```crystal +migrator.up_to(1_i64) +``` + +**Example** Listing applied migrations + +```crystal +migrator.print_applied_migrations +``` + +**Example** Listing pending migrations + +```crystal +migrator.print_pending_migrations +``` + +**Example** Listing rolled back migrations + +```crystal +migrator.print_rolled_back_migrations +``` + +**Example** Listing the last migration + +```crystal +migrator.last +``` + +## Constructors + +### def new`(schema : Schema)` + +## Instance Methods + +### def down + +### def schema + +### def up diff --git a/docs/cql-api/migrations/class-cql-migrator.md b/docs/cql-api/migrations/class-cql-migrator.md new file mode 100644 index 0000000..6c0ef9d --- /dev/null +++ b/docs/cql-api/migrations/class-cql-migrator.md @@ -0,0 +1,162 @@ +# class Cql::Migrator + +`Reference` < `Object` + +The `Migrator` class is used to manage migrations and provides methods to apply, rollback, and redo migrations. The `Migrator` class also provides methods to list applied and pending migrations. **Example** Creating a new migrator + +```crystal +schema = Cql::Schema.define(:northwind, "sqlite3://db.sqlite3") do |s| + table :schema_migrations do + primary :id, Int32 + column :name, String + column :version, Int64, index: true, unique: true + timestamps + end +end +migrator = Cql::Migrator.new(schema) +``` + +**Example** Applying migrations + +```crystal +migrator.up +``` + +## Constants + +### Log + +```crystal +::Log.for(self) +``` + +## Constructors + +### def new`(schema : Schema)` + +## Class Methods + +### def migrations + +### def migrations=`(migrations : Array(Migration.class))` + +## Instance Methods + +### def applied\_migrations + +Returns the applied migrations. + +* **@return** \[Array(MigrationRecord)] **Example** Listing applied migrations + +```crystal +migrator.applied_migrations +``` + +### def down`(steps : Int32 = Migrator.migrations.size)` + +Rolls back the last migration. + +* **@param** steps \[Int32] the number of migrations to roll back (default: 1) **Example** Rolling back migrations + +```crystal +migrator.down +``` + +### def down\_to`(version : Int64)` + +Rolls back to a specific migration version. + +* **@param** version \[Int64] the version to roll back to **Example** Rolling back to a specific version + +```crystal +migrator.down_to(1_i64) +``` + +### def last + +Returns the last migration. **Example** Listing the last migration + +```crystal +migrator.last +``` + +@return \[Migration.class | Nil] + +### def pending\_migrations + +Returns the pending migrations. + +* **@return** \[Array(MigrationRecord)] **Example** Listing pending migrations + +```crystal +migrator.pending_migrations +``` + +### def print\_applied\_migrations + +Prints the applied migrations. **Example** Listing applied migrations + +```crystal +migrator.print_applied_migrations +``` + +### def print\_pending\_migrations + +Prints the pending migrations. **Example** Listing pending migrations + +```crystal +migrator.print_pending_migrations +``` + +### def print\_rolled\_back\_migrations`(m : Array(Migration.class))` + +Prints the rolled back migrations. + +* **@param** m \[Array(Migration.class)] the migrations to print +* **@return** \[Nil] **Example** Listing rolled back migrations + +```crystal +migrator.print_rolled_back_migrations +``` + +### def redo + +Redoes the last migration. **Example** Redoing migrations + +```crystal +migrator.redo +``` + +### def repo + +### def rollback`(steps : Int32 = 1)` + +Rolls back the last migration. + +* **@param** steps \[Int32] the number of migrations to roll back (default: 1) **Example** Rolling back migrations + +```crystal +migrator.rollback +``` + +### def schema + +### def up`(steps : Int32 = Migrator.migrations.size)` + +Applies the pending migrations. + +* **@param** steps \[Int32] the number of migrations to apply (default: all) **Example** Applying migrations + +```crystal +migrator.up +``` + +### def up\_to`(version : Int64)` + +Applies migrations up to a specific version. + +* **@param** version \[Int64] the version to apply up to **Example** Applying to a specific version + +```crystal +migrator.up_to(1_i64) +``` diff --git a/docs/cql-api/migrations/cql-migrator-migrationrecord.md b/docs/cql-api/migrations/cql-migrator-migrationrecord.md new file mode 100644 index 0000000..d42edfd --- /dev/null +++ b/docs/cql-api/migrations/cql-migrator-migrationrecord.md @@ -0,0 +1,37 @@ +# Cql::Migrator::MigrationRecord + +## class Cql::Migrator::MigrationRecord + +`DB::Mappable` < `DB::Serializable` < `Reference` < `Object` + +Represents a migration record. @field id \[Int64] the migration record id @field name \[String] the migration name @field version \[Int64] the migration version @field created\_at \[Time] the creation time @field updated\_at \[Time] the update time **Example** Creating a migration record + +```crystal +record = Cql::MigrationRecord.new(0_i64, "CreateUsersTable", 1_i64) +``` + +## Included Modules + +`DB::Mappable`, `DB::Serializable` + +### Constructors + +#### def new`(id : Int64, name : String, version : Int64, created_at : Time = Time.local, updated_at : Time = Time.local)` + +#### def new`(rs : DB::ResultSet)` + +### Class Methods + +#### def from\_rs`(rs : DB::ResultSet)` + +### Instance Methods + +#### def created\_at + +#### def id + +#### def name + +#### def updated\_at + +#### def version diff --git a/docs/cql-api/module-cql-relations/README.md b/docs/cql-api/module-cql-relations/README.md new file mode 100644 index 0000000..2933210 --- /dev/null +++ b/docs/cql-api/module-cql-relations/README.md @@ -0,0 +1,2 @@ +# module Cql::Relations + diff --git a/docs/cql-api/module-cql-relations/class-cql-relations-collection-target-pk.md b/docs/cql-api/module-cql-relations/class-cql-relations-collection-target-pk.md new file mode 100644 index 0000000..0849cb9 --- /dev/null +++ b/docs/cql-api/module-cql-relations/class-cql-relations-collection-target-pk.md @@ -0,0 +1,271 @@ +# class Cql::Relations::Collection(Target, Pk) + +`Reference` < `Object` + +A collection of records for a one to many relationship This class is used to manage the relationship between two tables through a foreign key column in the target table and provide methods to manage the association between the two tables and query records in the associated table based on the foreign key value of the parent record. + +* **param** : Target (Cql::Model) - The target model +* **param** : Pk (Int64) - The primary key type +* **return** : Nil + +**Example** + +```crystal +class User + include Cql::Model(User, Int64) + property id : Int64 + property name : String + has_many :posts, Post, foreign_key: :user_id +end +``` + +## Constructors + +### def new`(key : Symbol, id : Pk, cascade : Bool = false, query : Cql::Query = (Cql::Query.new(Target.schema)).from(Target.table))` + +Initialize the many-to-many association collection class + +* **param** : key (Symbol) - The key for the parent record +* **param** : id (Pk) - The id value for the parent record +* **param** : target\_key (Symbol) - The key for the associated record +* **param** : cascade (Bool) - Delete associated records +* **param** : query (Cql::Query) - Query object +* **return** : ManyCollection + +**Example** + +```crystal +ManyCollection.new( + :movie_id, + 1, + :actor_id, + false, + Cql::Query.new(Actor.schema).from(Actor.table) +) +``` + +## Instance Methods + +### def <<`(record : Target)` + +Create a new record and associate it with the parent record if it doesn't exist + +* **param** : record (Target) +* **return** : Array(Target) + +**Example** + +```crystal +movie.actors << Actor.new(name: "Laurence Fishburne") +movie.actors.reload +movie.actors.all +=> [#] +``` + +### def all + +Create a new record and associate it with the parent record + +* **return** : Array(Target) + +**Example** + +```crystal +movie.actors.all +movie.actors.create(name: "Carrie-Anne Moss") +movie.actors.reload + +=> [#] +``` + +### def clear + +Clears all associated records from the parent record and the database + +* **return** : \[] of T + +**Example** + +```crystal +movie.actors.create(name: "Carrie-Anne Moss") +movie.actors.reload +movie.actors.all => 1 +movie.actors.clear +movie.actors.reload +movie.actors.all => [] +``` + +### def create`(record : Target)` + +Create a new record and associate it with the parent record + +* **param** : attributes (Hash(Symbol, String | Int64)) +* **return** : Array(Target) +* **raise** : Cql::Error + +**Example** + +```crystal +movie.actors.create!(name: "Hugo Weaving") +movie.actors.reload +movie.actors.all +=> [#] +``` + +### def create + +Create a new record and associate it with the parent record + +* **param** : attributes (Hash(Symbol, String | Int64)) +* **return** : Array(Target) +* **raise** : Cql::Error + +**Example** + +```crystal +movie.actors.create(name: "Carrie-Anne Moss") +movie.actors.reload +movie.actors.all +=> [#] +``` + +### def delete`(record : Target)` + +Delete the associated record from the parent record if it exists + +* **param** : record (Target) +* **return** : Bool + +**Example** + +```crystal +movie.actors.create(name: "Carrie-Anne Moss") +movie.actors.reload +movie.actors.all => 1 + +movie.actors.delete(Actor.find(1)) +movie.actors.reload +movie.actors.all + +=> [] of Actor +``` + +### def delete`(id : Pk)` + +Delete the associated record from the parent record if it exists + +* **param** : id (Pk) +* **return** : Bool + +**Example** + +```crystal +movie.actors.create(name: "Carrie-Anne Moss") +movie.actors.reload +movie.actors.all => 1 +movie.actors.delete(1) +movie.actors.reload +movie.actors.all => [] +``` + +### def empty? + +Check if the association is empty or not + +* **return** : Bool + +**Example** + +```crystal +movie.actors.empty? +=> true +``` + +### def exists? + +Check if the association exists or not based on the attributes provided + +* **param** : attributes (Hash(Symbol, String | Int64)) +* **return** : Bool + +**Example** + +```crystal +movie.actors.exists?(name: "Keanu Reeves") +=> true +``` + +### def find + +Find associated records based on the attributes provided for the parent record + +* **param** : attributes (Hash(Symbol, String | Int64)) +* **return** : Array(Target) + +**Example** + +```crystal +movie.actors.find(name: "Keanu Reeves") +=> [#] +``` + +### def ids + +Returns a list if primary keys for the associated records + +* **return** : Array(Pk) + +**Example** + +```crystal +movie.actors.ids +=> [1, 2, 3] +``` + +### def ids=`(ids : Array(Pk))` + +Associates the parent record with the records that match the primary keys provided + +* **param** : ids (Array(Pk)) +* **return** : Array(Target) + +**Example** + +```crystal +movie.actors.ids = [1, 2, 3] +movie.actors.reload +movie.actors.all => [ +#, + #, + #] +``` + +### def reload + +Reload the association records from the database and return them + +* **return** : Array(Target) + +**Example** + +```crystal +movie.actors.reload +=> [#] +``` + +### def size + +Returns the number of associated records for the parent record + +* **return** : Int64 + +**Example** + +```crystal +movie.actors.size +=> 1 +``` + +## Macros + +### macro method\_missing`(call)` diff --git a/docs/cql-api/module-cql-relations/class-cql-relations-manycollection-target-through-pk.md b/docs/cql-api/module-cql-relations/class-cql-relations-manycollection-target-through-pk.md new file mode 100644 index 0000000..7656280 --- /dev/null +++ b/docs/cql-api/module-cql-relations/class-cql-relations-manycollection-target-through-pk.md @@ -0,0 +1,173 @@ +# class Cql::Relations::ManyCollection(Target, Through, Pk) + +`Cql::Relations::Collection` < `Reference` < `Object` + +A collection of records for a many to many relationship This class is used to manage the relationship between two tables through a join table (through) + +A many-to-many association occurs when multiple records of one model can be associated with multiple records of another model, and vice versa. Typically, it requires a join table (or a junction table) to store the relationships between the records of the two models. + +Here’s how a many-to-many association is commonly implemented in CQL using Crystal. + +**Example** + +```crystal +class Movie + include Cql::Model(Movie, Int64) + + property id : Int64 + property title : String + + many_to_many :actors, Actor, join_through: :movies_actors +end + +class Actor + include Cql::Model(Actor, Int64) + property id : Int64 + property name : String +end + +class MoviesActors + include Cql::Model(MoviesActors, Int64) + property id : Int64 + property movie_id : Int64 + property actor_id : Int64 +end + +movie = Movie.create(title: "The Matrix") +actor = Actor.create(name: "Keanu Reeves") +``` + +## Constructors + +### def new`(key : Symbol, id : Pk, target_key : Symbol, cascade : Bool = false, query : Cql::Query = (Cql::Query.new(Target.schema)).from(Target.table))` + +Initialize the many-to-many association collection class + +* **param** : key (Symbol) - The key for the parent record +* **param** : id (Pk) - The id value for the parent record +* **param** : target\_key (Symbol) - The key for the associated record +* **param** : cascade (Bool) - Delete associated records +* **param** : query (Cql::Query) - Query object +* **return** : ManyCollection + +**Example** + +```crystal +ManyCollection.new( + :movie_id, + 1, + :actor_id, + false, + Cql::Query.new(Actor.schema).from(Actor.table) +) +``` + +## Instance Methods + +### def clear + +Clears all associated records from the parent record and the database + +* **return** : \[] of T + +**Example** + +```crystal +movie.actors.create(name: "Carrie-Anne Moss") +movie.actors.reload +movie.actors.all => 1 +movie.actors.clear +movie.actors.reload +movie.actors.all => [] +``` + +### def create`(record : Target)` + +Create a new record and associate it with the parent record + +* **param** : attributes (Hash(Symbol, String | Int64)) +* **return** : Array(Target) +* **raise** : Cql::Error + +**Example** + +```crystal +movie.actors.create!(name: "Hugo Weaving") +movie.actors.reload +movie.actors.all +=> [#] +``` + +### def create + +Create a new record and associate it with the parent record + +* **param** : attributes (Hash(Symbol, String | Int64)) +* **return** : Array(Target) +* **raise** : Cql::Error + +**Example** + +```crystal +movie.actors.create(name: "Carrie-Anne Moss") +movie.actors.reload +movie.actors.all +=> [#] +``` + +### def delete`(record : Target)` + +Delete the associated record from the parent record if it exists + +* **param** : record (Target) +* **return** : Bool + +**Example** + +```crystal +movie.actors.create(name: "Carrie-Anne Moss") +movie.actors.reload +movie.actors.all => 1 + +movie.actors.delete(Actor.find(1)) +movie.actors.reload +movie.actors.all + +=> [] of Actor +``` + +### def delete`(id : Pk)` + +Delete the associated record from the parent record if it exists + +* **param** : id (Pk) +* **return** : Bool + +**Example** + +```crystal +movie.actors.create(name: "Carrie-Anne Moss") +movie.actors.reload +movie.actors.all => 1 +movie.actors.delete(1) +movie.actors.reload +movie.actors.all => [] +``` + +### def ids=`(ids : Array(Int64))` + +Associates the parent record with the records that match the primary keys provided + +* **param** : ids (Array(Pk)) +* **return** : Array(Target) + +**Example** + +```crystal +movie.actors.ids = [1, 2, 3] +movie.actors.reload +movie.actors.all => [ +#, + #, + #] +``` diff --git a/docs/cql-api/module-cql-relations/module-cql-relations-belongsto.md b/docs/cql-api/module-cql-relations/module-cql-relations-belongsto.md new file mode 100644 index 0000000..932c7bd --- /dev/null +++ b/docs/cql-api/module-cql-relations/module-cql-relations-belongsto.md @@ -0,0 +1,7 @@ +# module Cql::Relations::BelongsTo + +## Macros + +### macro belongs\_to`(assoc, foreign_key)` + +Define the belongs\_to association diff --git a/docs/cql-api/module-cql-relations/module-cql-relations-hasmany.md b/docs/cql-api/module-cql-relations/module-cql-relations-hasmany.md new file mode 100644 index 0000000..d28cf31 --- /dev/null +++ b/docs/cql-api/module-cql-relations/module-cql-relations-hasmany.md @@ -0,0 +1,23 @@ +# module Cql::Relations::HasMany + +Define the has\_many association module that will be included in the model to define a one-to-many relationship between two tables in the database and provide methods to manage the association between the two tables and query records in the associated table based on the foreign key value of the parent record. + +* **param** : name (Symbol) - The name of the association +* **param** : type (Cql::Model) - The target model +* **param** : foreign\_key (Symbol) - The foreign key column in the target table +* **return** : Nil + +**Example** + +```crystal +class User + include Cql::Model(User, Int64) + property id : Int64 + property name : String + has_many :posts, Post, foreign_key: :user_id +end +``` + +## Macros + +### macro has\_many`(name, type, foreign_key, cascade = false)` diff --git a/docs/cql-api/module-cql-relations/module-cql-relations-hasone.md b/docs/cql-api/module-cql-relations/module-cql-relations-hasone.md new file mode 100644 index 0000000..828857e --- /dev/null +++ b/docs/cql-api/module-cql-relations/module-cql-relations-hasone.md @@ -0,0 +1,7 @@ +# module Cql::Relations::HasOne + +Define the has\_one association + +## Macros + +### macro has\_one`(name, type)` diff --git a/docs/cql-api/module-cql-relations/module-cql-relations-manytomany.md b/docs/cql-api/module-cql-relations/module-cql-relations-manytomany.md new file mode 100644 index 0000000..b188b43 --- /dev/null +++ b/docs/cql-api/module-cql-relations/module-cql-relations-manytomany.md @@ -0,0 +1,36 @@ +# module Cql::Relations::ManyToMany + +## Macros + +### macro many\_to\_many`(name, type, join_through, cascade = false)` + +Defines a many-to-many relationship between two models. This method will define a getter method that returns a ManyToMany::Collection. The collection can be used to add and remove records from the join table. + +* **param** : name (Symbol) - The name of the association +* **param** : type (Cql::Model) - The target model +* **param** : join\_through (Cql::Model) - The join table model +* **param** : cascade (Bool) - Delete associated records + +**Example** + +```crystal +class Movie + include Cql::Model(Movie, Int64) + property id : Int64 + property title : String + many_to_many :actors, Actor, join_through: :movies_actors +end + +class Actor + include Cql::Model(Actor, Int64) + property id : Int64 + property name : String +end + +class MoviesActors + include Cql::Model(MoviesActors, Int64) + property id : Int64 + property movie_id : Int64 + property actor_id : Int64 +end +```