txt: ## dist_zef-FCO-Red.md
  [![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/>
  
  ## Red - A **WiP** ORM for Raku
  
  ## INSTALL
  
  Install with (you need **rakudo 2018.12-94-g495ac7c00** or **newer**):
  
  ```
  zef install Red
  
  ```
  
  ## SYNOPSIS
  
  ```
  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<string>,
          :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;
  
  ```
  
  ```
  -- Equivalent to the following query:
  CREATE TABLE person(
      id integer NOT NULL primary key
      AUTOINCREMENT,
      name varchar(255) NOT NULL
  )
  
  ```
  
  ```
  Post.^create-table;
  
  ```
  
  ```
  -- 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)
  )
  
  ```
  
  ```
  my Post $post1 = Post.^load: :42id;
  
  ```
  
  ```
  -- 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
  
  ```
  
  ```
  my Post $post1 = Post.^load: 42;
  
  ```
  
  ```
  -- 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
  
  ```
  
  ```
  my Post $post1 = Post.^load: :title("my title");
  
  ```
  
  ```
  -- 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’
  
  ```
  
  ```
  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:
  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]
  
  ```
  
  ```
  say Person.new(:2id)
      .active-posts
      .grep: { .created > now }
  
  ```
  
  ```
  -- 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]
  
  ```
  
  ```
  my $now = now;
  say Person.new(:3id)
      .active-posts
      .grep: { .created > $now }
  
  ```
  
  ```
  -- 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
  --   )
  -- ]
  
  ```
  
  ```
  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:
  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: []
  
  ```
  
  ```
  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 post
  
  ```
  
  ```
  my $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:
  SELECT
      count(*) as "data_1"
  FROM
      post
  WHERE
      post.author_id = ?
  -- BIND: [1]
  
  ```
  
  ```
  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.
  
  ```
  # Single file e.g. Schema.pm6
  
  model Related { ... }
  
  
  # belongs to
  model MyModel {
      has Int     $!related-id is referencing( *.id, :model<Related> );
      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.
  
  ```
  # MyModel.pm6
  model MyModel {
      has Int     $!related-id is referencing{ :model<Related>, :column<id> };
      has         $.related    is relationship({ .id }, :model<Related>);
  }
  
  # Related.pm6
  model Related {
      has Int $.id is serial;
      has     @.my-models is relationship({ .related-id }, :model<MyModel>);
  }
  
  ```
  
  If Red can’t find where your `model` is defined you can override where it looks with `require`:
  
  ```
      has Int     $!related-id is referencing{ :model<Related>, :column<id>,
                                               :require<MyApp::Schema::Related> };
  
  ```
  
  #### custom table name
  
  ```
  model MyModel is table<custom_table_name> {}
  
  ```
  
  #### not nullable columns by default
  
  Red, by default, has not nullable columns, to change it:
  
  ```
  #| 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
  
  ```
  MyModel.^load: 42;
  MyModel.^load: id => 42;
  
  ```
  
  #### save object on the database
  
  ```
  $object.^save;
  
  ```
  
  #### search for a list of object
  
  ```
  Question.^all.grep: { .answer == 42 }; # returns a result seq
  
  ```
  
  #### phasers
  
  * `before-create`
  * `after-create`
  * `before-update`
  * `after-update`
  * `before-delete`
  * `after-delete`
  
  #### Temporary table
  
  ```
  model Bla is temp { ... }
  
  ```
  
  #### Create table
  
  ```
  Question.^create-table;
  Question.^create-table: :if-not-exists;
  Question.^create-table: :unless-exists;
  
  ```
  
  #### IN
  
  ```
  Question.^all.grep: *.answer ⊂ (3.14, 13, 42)
  
  ```
  
  #### create
  
  ```
  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<Bla>,
      :parent{:value<Ble>},
      :kids[
          {:value<Bli>},
          {:value<Blo>},
          {:value<Blu>}
      ]
  ;
  
  ```
  
  ## AUTHOR
  
  Fernando Correa de Oliveira [fernandocorrea@gmail.com](mailto:fernandocorrea@gmail.com)
  
  ## 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.
  