A delightful SQL ORM ☺️
Meet new Core rewritten from scratch! Featuring new schema declaration syntax, type-safe querying, spec splitting and overall code reduction!
#insert
, #update
, #delete
and all query other than #query
methods are removed in favour of type safety, thus closing #47 and closing #61 and also closing #33. Use Schema#insert
, Schema#update
and Schema#delete
instead#to_db
and #from_rs
methods via monkey patching, which closes #60Given SQL:
CREATE TABLE users(
uuid UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(100) NOT NULL,
age INT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE posts(
id SERIAL PRIMARY KEY,
author_uuid INT NOT NULL REFERENCES users (uuid),
content TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ
);
Crystal code:
require "pg"
require "core"
class User
include Core::Schema
schema users do
pkey uuid : UUID # UUIDs are supported out of the box
type name : String # Has NOT NULL in the column definition
type age : Union(Int32 | Nil) # Doesn't have NOT NULL in the column definition
type created_at : Time = DB::Default # Has DEFAULT in the column definition
type posts : Array(Post), foreign_key: "author_uuid" # That is an implicit reference
end
end
class Post
include Core::Schema
schema posts do
pkey id : Int32
type author : User, key: "author_id" # That is an explicit reference
type content : String
type created_at : Time = DB::Default
type updated_at : Union(Time | Nil)
end
end
Query
's #insert
, #join
, #order_by
, #returning
, #select
, #set
and #where
methods are now type-safe! It means that they would raise if passed invalid arguments in compilation time, e.g:
User.where(id: 42)
# Class 'User' doesn't have an attribute or reference with name 'id' defined in its schema eligible for 'Core::Query(User)#where' call
User.where(age: "18")
# Invalid type 'String' of argument 'age' for 'Core::Query(User)#where' call. Expected: 'Union(Int32 | Nil)'
user = User.new # Would raise because `name` is not null
user.insert # `Core::Query(User)` instance
# equals to
User.insert(name: "John")
user = repo.query(user.insert)
# or
repo.exec(user.insert)
Basically, in v0.4.0 Core became simpler and more abstract. 65 commits and 6033 line changes :+1: Read the changelog here: https://github.com/vladfaust/core/compare/v0.3.4...v0.4.0
Thanks to AngularJS commit style conventions, reading the changelog by commits is easy-peasy :lemon:
Repository#insert
returns inserted primary keyQuery.where
and Query.having
codeQuery.where
and Query.having
work by reference keysreference :author, key: :author_id
will add author_id
property. Note that if the key type differs from Int32
, you'll have to explicitly define it via key_type: Type
.JOIN
aliases with double quotes; fixes join(:user)
@ 6b2bf33265509796995e62ab1bf4b48f9205f0aeWHERE
and HAVING
by references @ 13beb13fc8720aebfa8f2a6b1c48f9b3ef4a1f3aversion
from README & version.cr
@ 036a46387ae8e0d3285d129cd9a1fb50db29089cConverters::DB::PG::Numeric
with non-precise Float64
conversion @ 3024d49dd29116c7416377b22f179b3ccc0618d8Schema
's field helpers @ d0789f82b1032a4682ea1f62e8beda9650cca17aConverters::JSON::TimeEpoch
@ ed91c313c9bba4f428e5ea35be3fe9055d987bc9