Take a look at our Documentation: https://fco.github.io/Red/
Install with (you need rakudo 2018.12-94-g495ac7c00 or newer):
zef install Red
If the command above doesn't work, you may need to add the following flag to the install command:
zef install --exclude="pq:ver<5>:from<native>" Red
The above --exclude flag tells zef to not try and install the dependency pq directly as it is a native library (it's referencing libpq.)
use Red:api<2>;
model Person {...}
model Post isrw {
hasInt$.idis serial;
hasInt$!author-idis referencing( *.id, :model(Person) );
hasStr$.titleis column{ :unique };
hasStr$.bodyis column;
has Person $.authoris relationship{ .author-id };
hasBool$.deletedis column =False;
hasDateTime$.createdis column .= now;
hasSet$.tagsis column{
:type<string>,
:deflate{ .keys.join:"," },
:inflate{ set(.split:",") }
} = set();
methoddelete { $!deleted=True; self.^save }
}
model Person isrw {
hasInt$.idis serial;
hasStr$.nameis column;
has Post @.postsis relationship{ .author-id };
methodactive-posts { @!posts.grep:not*.deleted }
}
my$*RED-DB= database "SQLite";
Person.^create-table;-- Equivalent to the following query:CREATETABLEperson(
id integerNOT NULLprimary key
AUTOINCREMENT,
name varchar(255) NOT NULL
)Post.^create-table;-- Equivalent to the following query:CREATETABLEpost(
id integerNOT NULLprimary key AUTOINCREMENT,
author_id integerNULLreferences person(id),
title varchar(255) NOT NULL,
body varchar(255) NOT NULL,
deleted integerNOT NULL,
created varchar(32) NOT NULL,
tags varchar(255) NOT NULL,
UNIQUE (title)
)my Post $post1= Post.^load::42id;-- Equivalent to the following query:SELECTpost.id,
post.author_idas"author-id",
post.title,
post.body,
post.deleted,
post.created,
post.tagsFROM
post
WHEREpost.id=42my Post $post1= Post.^load:42;-- Equivalent to the following query:SELECTpost.id,
post.author_idas"author-id",
post.title,
post.body,
post.deleted,
post.created,
post.tagsFROM
post
WHEREpost.id=42my Post $post1= Post.^load::title("my title");-- Equivalent to the following query:SELECTpost.id,
post.author_idas"author-id",
post.title,
post.body,
post.deleted,
post.created,
post.tagsFROM
post
WHEREpost.title= ‘my title’my$person= Person.^create::name<Fernando>;-- Equivalent to the following query:INSERT INTO person(
name
)
VALUES(
$1
) RETURNING *-- BIND: ["Fernando"]RETURNS:
Person.new(name =>"Fernando")say$person.posts;-- Equivalent to the following query:SELECTpost.id,
post.author_idas"author-id",
post.title,
post.body,
post.deleted,
post.created,
post.tagsFROM
post
WHEREpost.author_id= ?
-- BIND: [1]say Person.new(:2id)
.active-posts
.grep: { .created > now }-- Equivalent to the following query:SELECTpost.id,
post.author_idas"author-id",
post.title,
post.body,
post.deleted,
post.created,
post.tagsFROM
post
WHERE
(
post.author_id= ?
AND (
post.deleted==0ORpost.deleted IS NULL
)
)
ANDpost.created>1554246698.448671-- BIND: [2]my$now= now;
say Person.new(:3id)
.active-posts
.grep: { .created >$now }-- Equivalent to the following query:SELECTpost.id,
post.author_idas"author-id",
post.title,
post.body,
post.deleted,
post.created,
post.tagsFROM
post
WHERE
(
post.author_id= ?
AND (
post.deleted==0ORpost.deleted IS NULL
)
)
ANDpost.created> ?
-- BIND: [-- 3,-- Instant.from-posix(-- <399441421363/257>,-- Bool::False-- )-- ]Person.^create::name<Fernando>,
:posts[
{
:title("My new post"),
:body("A long post")
},
]
;-- 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"-- ]my$post= Post.^load::title("My new post");-- Equivalent to the following query:SELECTpost.id,
post.author_idas"author-id",
post.title,
post.body,
post.deleted,
post.created,
post.tagsFROM
post
WHEREpost.title= ‘My new post’
-- BIND: []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("")
)say$post.body;PRINTS:
A long postmy$author=$post.author;RETURNS:
Person.new(name =>"Fernando")$author.name="John Doe";
$author.^save;-- Equivalent to the following query:UPDATE person SET
name = ‘John Doe’
WHERE id =1$author.posts.create::title("Second post"),
:body("Another long post");-- 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-- ]$author.posts.elems;-- Equivalent to the following query:SELECTcount(*) as"data_1"FROM
post
WHEREpost.author_id= ?
-- BIND: [1]RETURNS:2Red is a WiP ORM for Raku.
is columnis column{}is idis id{}is serialis referencing{}is relationship{}is table<>is nullable
Red will infer relationship data if you use type constraints on your properties.
# Single file e.g. Schema.pm6
model Related { ... }
# belongs to
model MyModel {
hasInt$!related-idis referencing( *.id, :model<Related> );
has Related $.relatedis relationship{ .id };
}
# has one/has many
model Related {
hasInt$.idis serial;
has MyModel @.my-modelsis 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.
# MyModel.pm6
model MyModel {
hasInt$!related-idis referencing{ :model<Related>, :column<id> };
has$.relatedis relationship({ .id }, :model<Related>);
}
# Related.pm6
model Related {
hasInt$.idis serial;
has@.my-modelsis relationship({ .related-id }, :model<MyModel>);
}If Red can’t find where your model is defined you can override where it looks with require:
hasInt$!related-idis referencing{ :model<Related>, :column<id>,
:require<MyApp::Schema::Related> };model MyModel is table<custom_table_name> {}Red, by default, has not nullable columns, to change it:
#| This makes this model’s columns nullable by default
model MyModel is nullable {
hasInt$.col1is column; #= this column is nullablehasInt$.col2is column{ :!nullable }; #= this one is not nullable
}MyModel.^load:42;
MyModel.^load:id =>42;$object.^save;Question.^all.grep: { .answer ==42 }; # returns a result seqbefore-createafter-createbefore-updateafter-updatebefore-deleteafter-delete
model Bla istemp { ... }Question.^create-table;
Question.^create-table::if-not-exists;
Question.^create-table::unless-exists;Question.^all.grep:*.answer ⊂ (3.14, 13, 42)Post.^create::body("bla ble bli blo blu"), :title("qwer");
model Tree {
hasUInt$!idis id;
hasStr$.valueis column;
hasUInt$!parent-idis referencing{ Tree.id };
has Tree $.parentis relationship{ .parent-id };
has Tree @.kidsis relationship{ .parent-id };
}
Tree.^create-table::if-not-exists;
Tree.^create::value<Bla>,
:parent{:value<Ble>},
:kids[
{:value<Bli>},
{:value<Blo>},
{:value<Blu>}
]
;Fernando Correa de Oliveira fernandocorrea@gmail.com
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.