onyx-sql
Core
Crystal Object RElational Mapping you've been waiting for.
About
Tired of ActiveRecord's magic? Forget it. It's time for real programming!
Core is inspired by Crecto but more transparent and Crystal-ish.
Features:
- Use plain Crystal
Core::Model
s without ActiveRecord's magic methods; - Interact with Database via
Core::Repository
; - Build powerful queries with
Core::Query
; - Enjoy comprehensive documenation!
What Core does not:
- It doesn't do database migrations. Use micrate, for example;
- It doesn't have "handy" methods you probably got used to.
Need to count something? Use plain db#scalar.
Core::Repository
is for CRUD, not for endless utils.
Installation
Add this to your application's shard.yml
:
dependencies:
core:
github: vladfaust/core.cr
Usage
Assuming following initial database migration:
CREATE TABLE users(
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ
);
CREATE TABLE posts(
id SERIAL PRIMARY KEY,
author_id INT NOT NULL REFERENCES users (id),
content TEXT,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ
);
require "core"
require "db"
require "pg" # Or another driver
class User < Core::Model
schema do
primary_key :id
field :name, String, validate: {size: (3..32)}
reference :posts, Array(Post), foreign_key: :author_id
created_at_field :created_at
updated_at_field :updated_at
end
end
class Post < Core::Model
schema do
primary_key :id
field :content, String?
reference :author, User, key: :author_id
created_at_field :created_at
updated_at_field :updated_at
end
end
db = DB.open(ENV["DATABASE_URL"])
query_logger = Core::QueryLogger.new(STDOUT)
repo = Core::Repository.new(db, query_logger)
user = User.new(name: "Fo")
user.valid? # => false
user.errors # => [{:name => "must have size in range of 3..32"}]
user.name = "Foo"
user.id = repo.insert(user.valid!).as(Int64)
# INSERT INTO users (name, created_at) VALUES ($1, $2)
post = Post.new(author: user, content: "Foo Bar")
post.id = repo.insert(post).as(Int64)
# INSERT INTO posts (author_id, content, created_at) VALUES ($1, $2, $3)
alias Query = Core::Query
posts = repo.query(Query(Post).where(author: user))
# SELECT * FROM posts WHERE author_id = $1
posts.first.content # => "Foo Bar"
query = Query(User)
.join(:posts)
.group_by(%i(users.id posts.id))
.one
user = repo.query(query).first
# SELECT *
# FROM users JOIN posts AS posts ON posts.author_id = users.id
# GROUP BY users.id, posts.id LIMIT 1
user.name = "Bar"
user.changes # => {:name => "Bar"}
repo.update(user)
# UPDATE users SET name = $1 WHERE id = $2 RETURNING id
repo.delete(posts.first)
# DELETE FROM posts WHERE id = $1
Testing
- Apply migration from
./spec/migration.sql
- Run
env DATABASE_URL=your_database_url crystal spec
Contributing
- Fork it ( https://github.com/vladfaust/core.cr/fork )
- Create your feature branch (git checkout -b my-new-feature)
- Commit your changes (git commit -am 'Add some feature')
- Push to the branch (git push origin my-new-feature)
- Create a new Pull Request
Contributors
- @vladfaust Vlad Faust - creator, maintainer