Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/FCO/Red
A WiP ORM for Raku
https://github.com/FCO/Red
database db framework orm perl6 raku rakudo
Last synced: about 1 month ago
JSON representation
A WiP ORM for Raku
- Host: GitHub
- URL: https://github.com/FCO/Red
- Owner: FCO
- License: artistic-2.0
- Created: 2018-08-15T03:02:50.000Z (over 6 years ago)
- Default Branch: master
- Last Pushed: 2024-10-14T20:54:31.000Z (2 months ago)
- Last Synced: 2024-10-30T01:22:16.144Z (about 2 months ago)
- Topics: database, db, framework, orm, perl6, raku, rakudo
- Language: Raku
- Homepage:
- Size: 1.92 MB
- Stars: 69
- Watchers: 10
- Forks: 27
- Open Issues: 63
-
Metadata Files:
- Readme: README.md
- Changelog: Changes
- Contributing: CONTRIBUTING.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
- awesome-raku - Red - ORM for Raku. (Modules / Catalogue)
README
[![Build Status](https://github.com/FCO/Red/workflows/test/badge.svg)](https://github.com/FCO/Red/actions) [![Build Status](https://github.com/FCO/Red/workflows/ecosystem/badge.svg)](https://github.com/FCO/Red/actions) [![SparrowCI](https://ci.sparrowhub.io/project/gh-FCO-Red/badge)](https://ci.sparrowhub.io)
Red
===Take a look at our Documentation: [https://fco.github.io/Red/](https://fco.github.io/Red/)
Red - A **WiP** ORM for Raku
----------------------------INSTALL
-------Install with (you need **rakudo 2018.12-94-g495ac7c00** or **newer**):
zef install Red
SYNOPSIS
--------```raku
use Red:api<2>;model Person {...}
model Post is rw {
has Int $.id is serial;
has Int $!author-id is referencing( *.id, :model(Person) );
has Str $.title is column{ :unique };
has Str $.body is column;
has Person $.author is relationship{ .author-id };
has Bool $.deleted is column = False;
has DateTime $.created is column .= now;
has Set $.tags is column{
:type,
:deflate{ .keys.join: "," },
:inflate{ set(.split: ",") }
} = set();
method delete { $!deleted = True; self.^save }
}model Person is rw {
has Int $.id is serial;
has Str $.name is column;
has Post @.posts is relationship{ .author-id };
method active-posts { @!posts.grep: not *.deleted }
}my $*RED-DB = database "SQLite";
Person.^create-table;
``````sql
-- Equivalent to the following query:
CREATE TABLE person(
id integer NOT NULL primary key
AUTOINCREMENT,
name varchar(255) NOT NULL
)
``````raku
Post.^create-table;
``````sql
-- Equivalent to the following query:
CREATE TABLE post(
id integer NOT NULL primary key AUTOINCREMENT,
author_id integer NULL references person(id),
title varchar(255) NOT NULL,
body varchar(255) NOT NULL,
deleted integer NOT NULL,
created varchar(32) NOT NULL,
tags varchar(255) NOT NULL,
UNIQUE (title)
)
``````raku
my Post $post1 = Post.^load: :42id;
``````sql
-- Equivalent to the following query:
SELECT
post.id,
post.author_id as "author-id",
post.title,
post.body,
post.deleted,
post.created,
post.tags
FROM
post
WHERE
post.id = 42
``````raku
my Post $post1 = Post.^load: 42;
``````sql
-- Equivalent to the following query:
SELECT
post.id,
post.author_id as "author-id",
post.title,
post.body,
post.deleted,
post.created,
post.tags
FROM
post
WHERE
post.id = 42
``````raku
my Post $post1 = Post.^load: :title("my title");
``````sql
-- Equivalent to the following query:
SELECT
post.id,
post.author_id as "author-id",
post.title,
post.body,
post.deleted,
post.created,
post.tags
FROM
post
WHERE
post.title = ‘my title’
``````raku
my $person = Person.^create: :name;
``````sql
-- Equivalent to the following query:
INSERT INTO person(
name
)
VALUES(
$1
) RETURNING *
-- BIND: ["Fernando"]
``````raku
RETURNS:
Person.new(name => "Fernando")
``````raku
say $person.posts;
``````sql
-- Equivalent to the following query:
SELECT
post.id,
post.author_id as "author-id",
post.title,
post.body,
post.deleted,
post.created,
post.tags
FROM
post
WHERE
post.author_id = ?
-- BIND: [1]
``````raku
say Person.new(:2id)
.active-posts
.grep: { .created > now }
``````sql
-- Equivalent to the following query:
SELECT
post.id,
post.author_id as "author-id",
post.title,
post.body,
post.deleted,
post.created,
post.tags
FROM
post
WHERE
(
post.author_id = ?
AND (
post.deleted == 0
OR post.deleted IS NULL
)
)
AND post.created > 1554246698.448671
-- BIND: [2]
``````raku
my $now = now;
say Person.new(:3id)
.active-posts
.grep: { .created > $now }
``````sql
-- Equivalent to the following query:
SELECT
post.id,
post.author_id as "author-id",
post.title,
post.body,
post.deleted,
post.created,
post.tags
FROM
post
WHERE
(
post.author_id = ?
AND (
post.deleted == 0
OR post.deleted IS NULL
)
)
AND post.created > ?
-- BIND: [
-- 3,
-- Instant.from-posix(
-- <399441421363/257>,
-- Bool::False
-- )
-- ]
``````raku
Person.^create:
:name,
:posts[
{
:title("My new post"),
:body("A long post")
},
]
;
``````sql
-- Equivalent to the following query:
INSERT INTO person(
name
)
VALUES(
?
) RETURNING *
-- BIND: ["Fernando"]INSERT INTO post(
created,
title,
author_id,
tags,
deleted,
body
)
VALUES(
?,
?,
?,
?,
?,
?
) RETURNING *
-- BIND: [
-- "2019-04-02T22:55:13.658596+01:00",
-- "My new post",
-- 1,
-- "",
-- Bool::False,
-- "A long post"
-- ]
``````raku
my $post = Post.^load: :title("My new post");
``````sql
-- Equivalent to the following query:
SELECT
post.id,
post.author_id as "author-id",
post.title,
post.body,
post.deleted,
post.created,
post.tags
FROM
post
WHERE
post.title = ‘My new post’
-- BIND: []
``````raku
RETURNS:
Post.new(
title => "My new post",
body => "A long post",
deleted => 0,
created => DateTime.new(
2019,
4,
2,
23,
7,
46.677388,
:timezone(3600)
),
tags => Set.new("")
)
``````raku
say $post.body;
``````raku
PRINTS:
A long post
``````raku
my $author = $post.author;
``````raku
RETURNS:
Person.new(name => "Fernando")
``````raku
$author.name = "John Doe";$author.^save;
``````sql
-- Equivalent to the following query:
UPDATE person SET
name = ‘John Doe’
WHERE id = 1
``````raku
$author.posts.create:
:title("Second post"),
:body("Another long post");
``````sql
-- Equivalent to the following query:
INSERT INTO post(
title,
body,
created,
tags,
deleted,
author_id
)
VALUES(
?,
?,
?,
?,
?,
?
) RETURNING *
-- BIND: [
-- "Second post",
-- "Another long post",
-- "2019-04-02T23:28:09.346442+01:00",
-- "",
-- Bool::False,
-- 1
-- ]
``````raku
$author.posts.elems;
``````sql
-- Equivalent to the following query:
SELECT
count(*) as "data_1"
FROM
post
WHERE
post.author_id = ?
-- BIND: [1]
``````raku
RETURNS:
2
```DESCRIPTION
-----------Red is a *WiP* ORM for Raku.
### traits
* `is column`
* `is column{}`
* `is id`
* `is id{}`
* `is serial`
* `is referencing{}`
* `is relationship{}`
* `is table<>`
* `is nullable`
### features:
#### relationships
Red will infer relationship data if you use type constraints on your properties.
```raku
# Single file e.g. Schema.pm6model Related { ... }
# belongs to
model MyModel {
has Int $!related-id is referencing( *.id, :model );
has Related $.related is relationship{ .id };
}# has one/has many
model Related {
has Int $.id is serial;
has MyModel @.my-models is relationship{ .related-id };
}
```If you want to put your schema into multiple files, you can create an "indirect" relationship, and Red will look up the related models as necessary.
```raku
# MyModel.pm6
model MyModel {
has Int $!related-id is referencing{ :model, :column };
has $.related is relationship({ .id }, :model);
}# Related.pm6
model Related {
has Int $.id is serial;
has @.my-models is relationship({ .related-id }, :model);
}
```If Red can’t find where your `model` is defined you can override where it looks with `require`:
```raku
has Int $!related-id is referencing{ :model, :column,
:require };
```#### custom table name
```raku
model MyModel is table {}
```#### not nullable columns by default
Red, by default, has not nullable columns, to change it:
```raku
#| This makes this model’s columns nullable by default
model MyModel is nullable {
has Int $.col1 is column; #= this column is nullable
has Int $.col2 is column{ :!nullable }; #= this one is not nullable
}
```#### load object from database
```raku
MyModel.^load: 42;
MyModel.^load: id => 42;
```#### save object on the database
```raku
$object.^save;
```#### search for a list of object
```raku
Question.^all.grep: { .answer == 42 }; # returns a result seq
```#### phasers
* `before-create`
* `after-create`
* `before-update`
* `after-update`
* `before-delete`
* `after-delete`
#### Temporary table
```raku
model Bla is temp { ... }
```#### Create table
```raku
Question.^create-table;
Question.^create-table: :if-not-exists;
Question.^create-table: :unless-exists;
```#### IN
```raku
Question.^all.grep: *.answer ⊂ (3.14, 13, 42)
```#### create
```raku
Post.^create: :body("bla ble bli blo blu"), :title("qwer");model Tree {
has UInt $!id is id;
has Str $.value is column;
has UInt $!parent-id is referencing{ Tree.id };has Tree $.parent is relationship{ .parent-id };
has Tree @.kids is relationship{ .parent-id };
}Tree.^create-table: :if-not-exists;
Tree.^create:
:value,
:parent{:value},
:kids[
{:value},
{:value},
{:value}
]
;
```AUTHOR
------Fernando Correa de Oliveira
COPYRIGHT AND LICENSE
---------------------Copyright 2018 Fernando Correa de Oliveira
This library is free software; you can redistribute it and/or modify it under the Artistic License 2.0.