Model Usage

Multiple Connections

It is possible to register multiple connections, for example:
1
Granite::Connections << Granite::Adapter::Mysql.new(name: "legacy_db", url: "LEGACY_DB_URL")
2
Granite::Connections << Granite::Adapter::Pg.new(name: "new_db", url: "NEW_DB_URL")
3
4
class Foo < Granite::Base
5
connection legacy_db
6
7
# model fields
8
end
9
10
class Bar < Granite::Base
11
connection new_db
12
13
# model fields
14
end
Copied!
In this example, we defined two connections. One to a MySQL database named "legacy_db", and another to a PG database named "new_db". The connection name given in the model maps to the name of a registered connection.
NOTE: How you store/supply each connection's URL is up to you; Granite only cares that it gets registered via Granite::Connections << adapter_object.

timestamps

The timestamps macro defines created_at and updated_at field for you.
1
class Bar < Granite::Base
2
connection mysql
3
4
# Other fields
5
timestamps
6
end
Copied!
Would be equivalent to:
1
class Bar < Granite::Base
2
connection mysql
3
4
column created_at : Time?
5
column updated_at : Time?
6
end
Copied!

Primary Keys

Each model is required to have a primary key defined. Use the column macro with the primary: true option to denote the primary key.
NOTE: Composite primary keys are not yet supported.
1
class Site < Granite::Base
2
connection mysql
3
4
column id : Int64, primary: true
5
column name : String
6
end
Copied!
belongs_to associations can also be used as a primary key in much the same way.
1
class ChatSettings < Granite::Base
2
connection mysql
3
4
# chat_id would be the primary key
5
belongs_to chat : Chat, primary: true
6
end
Copied!

Custom

The name and type of the primary key can also be changed from the recommended id : Int64.
1
class Site < Granite::Base
2
connection mysql
3
4
column custom_id : Int32, primary: true
5
column name : String
6
end
Copied!

Natural Keys

Primary keys are defined as auto incrementing by default. For natural keys, you can set auto: false option.
1
class Site < Granite::Base
2
connection mysql
3
4
column custom_id : Int32, primary: true, auto: false
5
column name : String
6
end
Copied!

UUIDs

For databases that utilize UUIDs as the primary key, the type of the primary key can be set to UUID. This will generate a secure UUID when the model is saved.
1
class Book < Granite::Base
2
connection mysql
3
4
column isbn : UUID, primary: true
5
column name : String
6
end
7
8
book = Book.new
9
book.name = "Moby Dick"
10
book.isbn # => nil
11
book.save
12
book.isbn # => RFC4122 V4 UUID string
Copied!

Default values

A default value can be defined that will be used if another value is not specified/supplied.
1
class Book < Granite::Base
2
connection mysql
3
4
column id : Int64, primary: true
5
column name : String = "DefaultBook"
6
end
7
8
book = Book.new
9
book.name # => "DefaultBook"
Copied!

Generating Documentation

By default, running crystal docs will not include Granite methods, constants, and properties. To include these, use the granite_docs flag when generating the documentation. E.x. crystal docs -D granite_docs.
Doc block comments can be applied above the column macro.
1
# If the item is public.
2
column published : Bool
Copied!

Annotations

Annotations can be a powerful method of adding property specific features with minimal amounts of code. Since Granite utilizes the property keyword for its columns, annotations are able to be applied easily. These can either be JSON::Field, YAML::Field, or third party annotations.
1
class Foo < Granite::Base
2
connection mysql
3
table foos
4
5
column id : Int64, primary: true
6
7
@[JSON::Field(ignore: true)]
8
@[Bar::Settings(other_option: 7)]
9
column password : String
10
11
column name : String
12
column age : Int32
13
end
Copied!

Converters

Granite supports custom/special types via converters. Converters will convert the type into something the database can store when saving the model, and will convert the returned database value into that type on read.
Each converter has a T generic argument that tells the converter what type should be read from the DB::ResultSet. For example, if you wanted to use the JSON converter and your underlying database column is BLOB, you would use Bytes, if it was TEXT, you would use String.
Currently Granite supports various converters, each with their own supported database column types:
  • Enum(E, T) - Converts an Enum of type E to/from a database column of type T. Supported types for T are: Number, String, and Bytes.
  • Json(M, T) - Converters an Object of type M to/from a database column of type T. Supported types for T are: String, JSON::Any, and Bytes.
    • NOTE: M must implement #to_json and .from_json methods.
  • PgNumeric - Converts a PG::Numeric value to a Float64 on read.
The converter is defined on a per field basis. This example has an OrderStatus enum typed field. When saved, the enum value would be converted to a string to be stored in the DB. Then, when read, the string would be used to parse a new instance of OrderStatus.
1
enum OrderStatus
2
Active
3
Expired
4
Completed
5
end
6
7
class Order < Granite::Base
8
connection mysql
9
table foos
10
11
# Other fields
12
column status : OrderStatus, converter: Granite::Converters::Enum(OrderStatus, String)
13
end
Copied!

Serialization

Granite implements JSON::Serializable and YAML::Serializable by default. As such, models can be serialized to/from JSON/YAML via the #to_json/#to_yaml and .from_json/.from_yaml methods.
Last modified 1mo ago