modules/db/rowquill.zzm

rowquill-0.0.2 documentation

Package

Name
rowquill
Version
0.0.2
Uploaded
2026-06-15 20:42:01
Repository
https://github.com/tobyink/zuzu-rowquill
Dependencies
Metadata
zuzu-distribution.json
Archive
Download .tar.gz

NAME

db/rowquill - Small row-object ORM for ZuzuScript.

SYNOPSIS

  from db/rowquill import Schema;
  from std/db import DB;

  let schema := new Schema( dbh: DB.temp() );

  schema.add_table( "employee", function ( tab ) {
      tab.add_column( "id", "int", primary: true );
      tab.add_column( "name", "varchar", required: true );
  } );

  let bob := schema.table("employee").create( id: 1, name: "Bob" );
  bob.insert();

  say( schema.table("employee").find(1).name() );

DESCRIPTION

db/rowquill is a small row-object ORM for ZuzuScript. A Schema wraps a std/db database handle and builds one generated class per table. Generated row classes support create, find, search, insert, update, delete, column accessors, inflate/deflate callbacks, validation, and simple has_one and has_many relationships. Table builders can also add custom row helper methods and static table-class helper methods.

Schema Helpers

Schema.has_table checks whether a table has been registered, and Schema.table_names returns the registered table names. Schema.find, Schema.search, Schema.create, and Schema.insert are convenience wrappers around the generated table class for cases where the table name is dynamic.

Searching

search accepts named column conditions. Operator conditions are arrays containing the SQL-ish operator and value. Supported operators include LIKE, NOT LIKE, ILIKE, IN, NOT IN, and BETWEEN.

  let Employee := schema.table("employee");
  Employee.search(
      name: [ "ILIKE", "%rob%" ],
      id:   [ "IN", [ 1, 2, 3 ] ],
      opts: { order_by: [ [ "name", "ASC" ] ], limit: 20 },
  );

Condition groups may be nested with AND, OR, and NOT. Query options live under opts. Convenience methods all, first, count, exists, find_or_create, and create_or_update are also available.

Relationship joins are declared as mappings from columns on the current row to columns on the related table. Multiple mappings are combined with AND. Relationships may also include a where condition using the same condition style as search.

  tab.has_many(
      accessor: "employees",
      table:    "employee",
      join:     { id: "dept", company: "company" },
	      where:    { is_deleted: false },
	  );

A non-narrowed has_one relationship may be assigned with relationship(set: row); this copies every join column from the related row. has_many relationships and relationships with where filters cannot be assigned this way.

RDF Export

When the optional rdf distribution is available, as_rdf exports a row as RDF quads using the W3C Direct Mapping shape. all_as_rdf on a table class exports every row in that table, and Schema.all_as_rdf exports every registered table. Passing into: store adds the quads to an RDF store and returns the store.

  let schema := new Schema(
      dbh: DB.temp(),
      base_uri: "http://foo.example/DB/",
  );

  let quads := schema.table("employee").find(42).as_rdf();
  schema.table("employee").all_as_rdf( into: store );
  schema.all_as_rdf( into: store );

RDF export uses Rowquill metadata as the schema source of truth. Columns marked with primary: true identify rows, non-null column values become literal triples, and has_one relationships generate #ref- predicates when all local join columns are non-null. If rdf is unavailable or the schema has no base_uri, RDF export throws a runtime error.

on_rdf registers a table-level callback for additional RDF quads. The callback receives the row and must return an array of quads to append to the default generated quads.

  tab.on_rdf( function ( employee ) {
      return [
          rdf_quad(
              employee._rdf_row_node(),
              rdf_iri("http://example.com/audit"),
              rdf_literal("custom"),
          ),
      ];
  } );

Helper Methods

add_helper adds an instance method to generated row objects. The callback receives the row object as its first argument, followed by any arguments passed to the generated method.

  tab.add_helper( "is_accountant", function ( employee ) {
      return employee.department().name() eq "Accounts";
	  } );

add_static adds a static method to the generated table class. The callback receives the table class as its first argument.

  tab.add_static( "named", function ( Employee, String name ) {
      return Employee.search( name: name );
	  } );

add_trait composes a custom trait into the generated row class. Traits are useful when several tables should share row-object behaviour.

  trait HasSlug {
      method slug () {
          return lc self.name();
      }
  }

  tab.add_trait( HasSlug );

Column Options

Column options include primary, accessor, inflate, deflate, length, pattern, validate, required, default, readonly, unique, and exists_in. Multiple length, pattern, and validate options are honoured.

default may be a value or callback. readonly prevents a column from being changed after insertion. unique checks for an existing non-null value before writes. exists_in checks a non-null value against another table and column using a table.column string.

Hooks

Table builders can register lifecycle hooks. Each callback receives the row object. Supported hooks are before_insert, after_insert, before_update, after_update, before_delete, and after_delete.

Transactions

Schema.transaction runs a callback inside a transaction and returns the callback result. Exceptions roll the transaction back and are rethrown. Nested transactions use savepoints.

EXPORTS

Schema

The main ORM class.

ClassMaker

The table builder class passed to Schema.add_table. This is exported mostly for subclassing and advanced integration.

COPYRIGHT AND LICENCE

db/rowquill is copyright Toby Inkster.

It is free software; you may redistribute it and/or modify it under the terms of either the Artistic License 1.0 or the GNU General Public License version 2.