{"id":13734324,"url":"https://github.com/FCO/Red","last_synced_at":"2025-05-08T10:31:22.518Z","repository":{"id":33906465,"uuid":"144798831","full_name":"FCO/Red","owner":"FCO","description":"A WiP ORM for Raku","archived":false,"fork":false,"pushed_at":"2024-10-14T20:54:31.000Z","size":2010,"stargazers_count":69,"open_issues_count":63,"forks_count":27,"subscribers_count":10,"default_branch":"master","last_synced_at":"2024-10-30T01:22:16.144Z","etag":null,"topics":["database","db","framework","orm","perl6","raku","rakudo"],"latest_commit_sha":null,"homepage":"","language":"Raku","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"artistic-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/FCO.png","metadata":{"files":{"readme":"README.md","changelog":"Changes","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"FCO"}},"created_at":"2018-08-15T03:02:50.000Z","updated_at":"2024-10-14T20:54:34.000Z","dependencies_parsed_at":"2023-01-15T03:16:42.232Z","dependency_job_id":"b6de178a-ff2f-45e6-b1d6-658905caa145","html_url":"https://github.com/FCO/Red","commit_stats":{"total_commits":1341,"total_committers":32,"mean_commits":41.90625,"dds":0.3557046979865772,"last_synced_commit":"7ab1fc52b271713b5e2aa49cd9f2ab54c4589ce4"},"previous_names":[],"tags_count":56,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FCO%2FRed","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FCO%2FRed/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FCO%2FRed/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FCO%2FRed/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/FCO","download_url":"https://codeload.github.com/FCO/Red/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253045702,"owners_count":21845760,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["database","db","framework","orm","perl6","raku","rakudo"],"created_at":"2024-08-03T03:00:54.648Z","updated_at":"2025-05-08T10:31:21.959Z","avatar_url":"https://github.com/FCO.png","language":"Raku","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)\n\nRed\n===\n\nTake a look at our Documentation: [https://fco.github.io/Red/](https://fco.github.io/Red/)\n\nRed - A **WiP** ORM for Raku\n----------------------------\n\nINSTALL\n-------\n\nInstall with (you need **rakudo 2018.12-94-g495ac7c00** or **newer**):\n\n    zef install Red\n\nSYNOPSIS\n--------\n\n```raku\nuse Red:api\u003c2\u003e;\n\nmodel Person {...}\n\nmodel Post is rw {\n    has Int         $.id        is serial;\n    has Int         $!author-id is referencing( *.id, :model(Person) );\n    has Str         $.title     is column{ :unique };\n    has Str         $.body      is column;\n    has Person      $.author    is relationship{ .author-id };\n    has Bool        $.deleted   is column = False;\n    has DateTime    $.created   is column .= now;\n    has Set         $.tags      is column{\n        :type\u003cstring\u003e,\n        :deflate{ .keys.join: \",\" },\n        :inflate{ set(.split: \",\") }\n    } = set();\n    method delete { $!deleted = True; self.^save }\n}\n\nmodel Person is rw {\n    has Int  $.id            is serial;\n    has Str  $.name          is column;\n    has Post @.posts         is relationship{ .author-id };\n    method active-posts { @!posts.grep: not *.deleted }\n}\n\nmy $*RED-DB = database \"SQLite\";\n\nPerson.^create-table;\n```\n\n```sql\n-- Equivalent to the following query:\nCREATE TABLE person(\n    id integer NOT NULL primary key\n    AUTOINCREMENT,\n    name varchar(255) NOT NULL\n)\n```\n\n```raku\nPost.^create-table;\n```\n\n```sql\n-- Equivalent to the following query:\nCREATE TABLE post(\n    id integer NOT NULL primary key AUTOINCREMENT,\n    author_id integer NULL references person(id),\n    title varchar(255) NOT NULL,\n    body varchar(255) NOT NULL,\n    deleted integer NOT NULL,\n    created varchar(32) NOT NULL,\n    tags varchar(255) NOT NULL,\n    UNIQUE (title)\n)\n```\n\n```raku\nmy Post $post1 = Post.^load: :42id;\n```\n\n```sql\n-- Equivalent to the following query:\nSELECT\n    post.id,\n    post.author_id as \"author-id\",\n    post.title,\n    post.body,\n    post.deleted,\n    post.created,\n    post.tags\nFROM\n    post\nWHERE\n    post.id = 42\n```\n\n```raku\nmy Post $post1 = Post.^load: 42;\n```\n\n```sql\n-- Equivalent to the following query:\nSELECT\n    post.id,\n    post.author_id as \"author-id\",\n    post.title,\n    post.body,\n    post.deleted,\n    post.created,\n    post.tags\nFROM\n    post\nWHERE\n    post.id = 42\n```\n\n```raku\nmy Post $post1 = Post.^load: :title(\"my title\");\n```\n\n```sql\n-- Equivalent to the following query:\nSELECT\n    post.id,\n    post.author_id as \"author-id\",\n    post.title,\n    post.body,\n    post.deleted,\n    post.created,\n    post.tags\nFROM\n    post\nWHERE\n    post.title = ‘my title’\n```\n\n```raku\nmy $person = Person.^create: :name\u003cFernando\u003e;\n```\n\n```sql\n-- Equivalent to the following query:\nINSERT INTO person(\n    name\n)\nVALUES(\n    $1\n) RETURNING *\n-- BIND: [\"Fernando\"]\n```\n\n```raku\nRETURNS:\nPerson.new(name =\u003e \"Fernando\")\n```\n\n```raku\nsay $person.posts;\n```\n\n```sql\n-- Equivalent to the following query:\nSELECT\n    post.id,\n    post.author_id as \"author-id\",\n    post.title,\n    post.body,\n    post.deleted,\n    post.created,\n    post.tags\nFROM\n    post\nWHERE\n    post.author_id = ?\n-- BIND: [1]\n```\n\n```raku\nsay Person.new(:2id)\n    .active-posts\n    .grep: { .created \u003e now }\n```\n\n```sql\n-- Equivalent to the following query:\nSELECT\n    post.id,\n    post.author_id as \"author-id\",\n    post.title,\n    post.body,\n    post.deleted,\n    post.created,\n    post.tags\nFROM\n    post\nWHERE\n    (\n       post.author_id = ?\n       AND (\n           post.deleted == 0\n           OR post.deleted IS NULL\n       )\n    )\n    AND post.created \u003e 1554246698.448671\n-- BIND: [2]\n```\n\n```raku\nmy $now = now;\nsay Person.new(:3id)\n    .active-posts\n    .grep: { .created \u003e $now }\n```\n\n```sql\n-- Equivalent to the following query:\nSELECT\n    post.id,\n    post.author_id as \"author-id\",\n    post.title,\n    post.body,\n    post.deleted,\n    post.created,\n    post.tags\nFROM\n    post\nWHERE\n    (\n       post.author_id = ?\n       AND (\n           post.deleted == 0\n           OR post.deleted IS NULL\n       )\n    )\n    AND post.created \u003e ?\n-- BIND: [\n--   3,\n--   Instant.from-posix(\n--       \u003c399441421363/257\u003e,\n--       Bool::False\n--   )\n-- ]\n```\n\n```raku\nPerson.^create:\n    :name\u003cFernando\u003e,\n    :posts[\n        {\n            :title(\"My new post\"),\n            :body(\"A long post\")\n        },\n    ]\n;\n```\n\n```sql\n-- Equivalent to the following query:\nINSERT INTO person(\n    name\n)\nVALUES(\n    ?\n) RETURNING *\n-- BIND: [\"Fernando\"]\n\nINSERT INTO post(\n    created,\n    title,\n    author_id,\n    tags,\n    deleted,\n    body\n)\nVALUES(\n    ?,\n    ?,\n    ?,\n    ?,\n    ?,\n    ?\n) RETURNING *\n-- BIND: [\n--   \"2019-04-02T22:55:13.658596+01:00\",\n--   \"My new post\",\n--   1,\n--   \"\",\n--   Bool::False,\n--   \"A long post\"\n-- ]\n```\n\n```raku\nmy $post = Post.^load: :title(\"My new post\");\n```\n\n```sql\n-- Equivalent to the following query:\nSELECT\n    post.id,\n    post.author_id as \"author-id\",\n    post.title,\n    post.body,\n    post.deleted,\n    post.created,\n    post.tags\nFROM\n    post\nWHERE\n    post.title = ‘My new post’\n-- BIND: []\n```\n\n```raku\nRETURNS:\nPost.new(\n   title   =\u003e \"My new post\",\n   body    =\u003e \"A long post\",\n   deleted =\u003e 0,\n   created =\u003e DateTime.new(\n       2019,\n       4,\n       2,\n       23,\n       7,\n       46.677388,\n       :timezone(3600)\n   ),\n   tags    =\u003e Set.new(\"\")\n)\n```\n\n```raku\nsay $post.body;\n```\n\n```raku\nPRINTS:\nA long post\n```\n\n```raku\nmy $author = $post.author;\n```\n\n```raku\nRETURNS:\nPerson.new(name =\u003e \"Fernando\")\n```\n\n```raku\n$author.name = \"John Doe\";\n\n$author.^save;\n```\n\n```sql\n-- Equivalent to the following query:\nUPDATE person SET\n    name = ‘John Doe’\nWHERE id = 1\n```\n\n```raku\n$author.posts.create:\n    :title(\"Second post\"),\n    :body(\"Another long post\");\n```\n\n```sql\n-- Equivalent to the following query:\nINSERT INTO post(\n    title,\n    body,\n    created,\n    tags,\n    deleted,\n    author_id\n)\nVALUES(\n    ?,\n    ?,\n    ?,\n    ?,\n    ?,\n    ?\n) RETURNING *\n-- BIND: [\n--   \"Second post\",\n--   \"Another long post\",\n--   \"2019-04-02T23:28:09.346442+01:00\",\n--   \"\",\n--   Bool::False,\n--   1\n-- ]\n```\n\n```raku\n$author.posts.elems;\n```\n\n```sql\n-- Equivalent to the following query:\nSELECT\n    count(*) as \"data_1\"\nFROM\n    post\nWHERE\n    post.author_id = ?\n-- BIND: [1]\n```\n\n```raku\nRETURNS:\n2\n```\n\nDESCRIPTION\n-----------\n\nRed is a *WiP* ORM for Raku.\n\n### traits\n\n  * `is column`\n\n  * `is column{}`\n\n  * `is id`\n\n  * `is id{}`\n\n  * `is serial`\n\n  * `is referencing{}`\n\n  * `is relationship{}`\n\n  * `is table\u003c\u003e`\n\n  * `is nullable`\n\n### features:\n\n#### relationships\n\nRed will infer relationship data if you use type constraints on your properties.\n\n```raku\n# Single file e.g. Schema.pm6\n\nmodel Related { ... }\n\n\n# belongs to\nmodel MyModel {\n    has Int     $!related-id is referencing( *.id, :model\u003cRelated\u003e );\n    has Related $.related    is relationship{ .id };\n}\n\n# has one/has many\nmodel Related {\n    has Int $.id is serial;\n    has MyModel @.my-models is relationship{ .related-id };\n}\n```\n\nIf 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.\n\n```raku\n# MyModel.pm6\nmodel MyModel {\n    has Int     $!related-id is referencing{ :model\u003cRelated\u003e, :column\u003cid\u003e };\n    has         $.related    is relationship({ .id }, :model\u003cRelated\u003e);\n}\n\n# Related.pm6\nmodel Related {\n    has Int $.id is serial;\n    has     @.my-models is relationship({ .related-id }, :model\u003cMyModel\u003e);\n}\n```\n\nIf Red can’t find where your `model` is defined you can override where it looks with `require`:\n\n```raku\n    has Int     $!related-id is referencing{ :model\u003cRelated\u003e, :column\u003cid\u003e,\n                                             :require\u003cMyApp::Schema::Related\u003e };\n```\n\n#### custom table name\n\n```raku\nmodel MyModel is table\u003ccustom_table_name\u003e {}\n```\n\n#### not nullable columns by default\n\nRed, by default, has not nullable columns, to change it:\n\n```raku\n#| This makes this model’s columns nullable by default\nmodel MyModel is nullable {\n    has Int $.col1 is column;               #= this column is nullable\n    has Int $.col2 is column{ :!nullable }; #= this one is not nullable\n}\n```\n\n#### load object from database\n\n```raku\nMyModel.^load: 42;\nMyModel.^load: id =\u003e 42;\n```\n\n#### save object on the database\n\n```raku\n$object.^save;\n```\n\n#### search for a list of object\n\n```raku\nQuestion.^all.grep: { .answer == 42 }; # returns a result seq\n```\n\n#### phasers\n\n  * `before-create`\n\n  * `after-create`\n\n  * `before-update`\n\n  * `after-update`\n\n  * `before-delete`\n\n  * `after-delete`\n\n#### Temporary table\n\n```raku\nmodel Bla is temp { ... }\n```\n\n#### Create table\n\n```raku\nQuestion.^create-table;\nQuestion.^create-table: :if-not-exists;\nQuestion.^create-table: :unless-exists;\n```\n\n#### IN\n\n```raku\nQuestion.^all.grep: *.answer ⊂ (3.14, 13, 42)\n```\n\n#### create\n\n```raku\nPost.^create: :body(\"bla ble bli blo blu\"), :title(\"qwer\");\n\n\nmodel Tree {\n    has UInt   $!id        is id;\n    has Str    $.value     is column;\n    has UInt   $!parent-id is referencing{ Tree.id };\n\n    has Tree   $.parent    is relationship{ .parent-id };\n    has Tree   @.kids      is relationship{ .parent-id };\n}\n\nTree.^create-table: :if-not-exists;\n\nTree.^create:\n    :value\u003cBla\u003e,\n    :parent{:value\u003cBle\u003e},\n    :kids[\n        {:value\u003cBli\u003e},\n        {:value\u003cBlo\u003e},\n        {:value\u003cBlu\u003e}\n    ]\n;\n```\n\nAUTHOR\n------\n\nFernando Correa de Oliveira \u003cfernandocorrea@gmail.com\u003e\n\nCOPYRIGHT AND LICENSE\n---------------------\n\nCopyright 2018 Fernando Correa de Oliveira\n\nThis library is free software; you can redistribute it and/or modify it under the Artistic License 2.0.\n\n","funding_links":["https://github.com/sponsors/FCO"],"categories":["Modules"],"sub_categories":["Catalogue"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FFCO%2FRed","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FFCO%2FRed","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FFCO%2FRed/lists"}