onyx-sql

DB-agnostic SQL ORM with beautiful DSL and type-safe Query builder orm sql sql-orm query-builder crystal-db onyxframework
0.2.3 released
onyxframework/sql
90 6 24
Onyx Framework

Core

Crystal Object RElational Mapping you've been waiting for.

Build Status Docs Dependency Status GitHub release

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::Models 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
    version: ~> 0.2.1

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        NOT NULL,
  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
    virtual_field :posts_count, Int64
    reference :posts, Array(Post), foreign_key: :author_id
    created_at_field :created_at
    updated_at_field :updated_at
  end

  validation do
    errors.push({:name => "length must be >= 3"}) unless name.try &.size.>= 3
  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 => "length must be >= 3"}]
user.name = "Foo"
user.valid? # => true

user.id = repo.insert(user) # See ^1
# INSERT INTO users (name, created_at) VALUES ($1, $2) RETURNING id

post = Post.new(author: user, content: "Foo Bar")
post.id = repo.insert(post) # See ^1
# INSERT INTO posts (author_id, content, created_at) VALUES ($1, $2, $3) RETURNING id

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)
  .select(:*, :"COUNT(posts.id) AS posts_count")
  .group_by(%i(users.id posts.id))
  .one
user = repo.query(query).first
# SELECT *, COUNT (posts.id) AS posts_count
# FROM users JOIN posts AS posts ON posts.author_id = users.id
# GROUP BY users.id, posts.id LIMIT 1

user.posts_count # => 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

^1: Returning IDs is not working for PostgreSQL yet. See https://github.com/will/crystal-pg/issues/112.

Testing

  1. Apply migration from ./spec/migration.sql
  2. Run env DATABASE_URL=your_database_url crystal spec

Contributing

  1. Fork it ( https://github.com/vladfaust/core.cr/fork )
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request

Contributors

onyx-sql:
  github: onyxframework/sql
  version: ~> 0.2.3
License MIT
Crystal 0.23.1

Authors

Dependencies 2

  • db ~> 0.4.2
    {'github' => 'crystal-lang/crystal-db', 'version' => '~> 0.4.2'}
  • time_format ~> 0.1.0
    {'github' => 'vladfaust/time_format.cr', 'version' => '~> 0.1.0'}

Development Dependencies 1

  • pg ~> 0.13.3
    {'github' => 'will/crystal-pg', 'version' => '~> 0.13.3'}

Dependents 3

Last synced .
search fire star recently