trait TableClass {
method _table_class () {
return self.get_schema().table( self.get_table_name() );
}
method _column ( String col ) {
for ( let c in self.get_column_metadata() ) {
return c if c{name} eq col;
return c if c.get( "accessor", "" ) eq col;
}
die "No such column: " _ col;
}
method _inflate_column ( String col, value ) {
let c := self._column(col);
return c{inflate}(value) if c.exists("inflate");
return value;
}
method _deflate_column ( String col, value ) {
let c := self._column(col);
return c{deflate}(value) if c.exists("deflate");
return value;
}
method _validate_column ( String col, value, raw_value ) {
let c := self._column(col);
if ( c.get( "required", false ) and raw_value ≡ null ) {
die "Column " _ col _ " is required";
}
for ( let max_length in c.get_all("length") ) {
if ( length raw_value > max_length ) {
die "Column " _ col _
" exceeds maximum length " _ max_length;
}
}
for ( let pattern in c.get_all("pattern") ) {
if ( not ( raw_value ~ pattern ) ) {
die "Column " _ col _ " does not match pattern";
}
}
for ( let validator in c.get_all("validate") ) {
if ( not validator(value) ) {
die "Column " _ col _ " failed validation";
}
}
return true;
}
method _set_column ( String col, value, Boolean raw := false ) {
let c := self._column(col);
if ( c.get( "readonly", false ) and self{in_database} ) {
die "Column " _ col _ " is read-only";
}
let raw_value := raw
? value
: self._deflate_column( c{name}, value );
let validators := c.get_all("validate");
let value_for_validation := raw and validators.length()
? self._inflate_column( c{name}, raw_value )
: value;
self._validate_column(
c{name},
value_for_validation,
raw_value
);
self{column_data}{(c{name})} := raw_value;
self{dirty}{(c{name})} := true;
return self;
}
method _get_column ( String col, Boolean raw := false ) {
let value := self{column_data}{(col)};
return raw ? value : self._inflate_column( col, value );
}
method _relationship_conditions ( rel ) {
let conditions := {};
for ( let pair in rel{join}.to_Array() ) {
let local_col := self._column(pair.key){name};
let value := self._get_column(local_col);
return null if value ≡ null;
conditions{(pair.value)} := value;
}
if ( rel.exists("where") ) {
return { AND: [ conditions, rel{where} ] };
}
return conditions;
}
method _set_relationship ( rel, related ) {
die "Cannot set has_many relationship " _ rel{accessor}
if rel{type} eq "has_many";
die "Cannot set narrowed relationship " _ rel{accessor}
if rel.exists("where");
for ( let pair in rel{join}.to_Array() ) {
let local_col := self._column(pair.key){name};
let related_col := related._column(pair.value){name};
self._set_column(
local_col,
related._get_column( related_col )
);
}
return self;
}
method _validate_database_constraints ( Array cols ) {
for ( let col in cols ) {
let c := self._column(col);
let value := self{column_data}.get( c{name}, null );
next if value ≡ null;
if ( c.get( "unique", false ) ) {
self._validate_unique_column(c, value);
}
if ( c.exists("exists_in") ) {
self._validate_exists_in(c, value);
}
}
return true;
}
method _validate_required_columns_for_write () {
for ( let c in self.get_column_metadata() ) {
let col := c{name};
if ( c.get( "required", false ) ) {
if (
not self{column_data}.exists(col)
or self{column_data}{(col)} ≡ null
) {
die "Column " _ col _ " is required";
}
}
}
return true;
}
method _has_one ( rel ) {
let conditions := self._relationship_conditions(rel);
return null if conditions ≡ null;
let rows := self.get_schema()
.table(rel{table})
._search_run(conditions, {});
return rows[0] if rows.length() > 0;
return null;
}
method _has_many ( rel ) {
let conditions := self._relationship_conditions(rel);
return [] if conditions ≡ null;
return self.get_schema()
.table(rel{table})
._search_run(conditions, {});
}
method _apply_defaults () {
for ( let c in self.get_column_metadata() ) {
next if not c.exists("default");
next if self{column_data}.exists(c{name});
let value := c{default} instanceof Function
? c{default}(self)
: c{default};
self._set_column( c{name}, value );
}
return self;
}
method _run_hooks ( String name ) {
for ( let hook in self.get_hook_metadata(){(name)} ) {
hook(self);
}
return self;
}
method _validate_unique_column ( column, value ) {
from std/string import join, sprint;
let binds := [ value ];
let where := [
self._table_class()._sql_identifier(column{name}) _ "=?",
];
if ( self{in_database} ) {
for ( let pkey_name in self._table_class()._primary_key_names() ) {
die "Missing primary key value for " _ pkey_name
if not self{column_data}.exists(pkey_name);
where.push(
self._table_class()._sql_identifier(pkey_name) _ "!=?"
);
binds.push( self{column_data}{(pkey_name)} );
}
}
let sql := sprint(
"SELECT 1 AS found FROM %s WHERE %s",
self._table_class()._sql_table(),
join( " AND ", where ),
);
let sth := self.get_schema().get_dbh().prepare(sql);
sth.execute( ... binds );
die "Column " _ column{name} _ " must be unique"
if sth.next_typed_dict();
return true;
}
method _validate_exists_in ( column, value ) {
from std/string import split, sprint;
let spec := column{exists_in};
let parts := split( spec, ".", 2 );
die "Column " _ column{name} _
" exists_in must be table.column"
if parts.length() ≢ 2
or not ( parts[0] ~ /^[A-Za-z_][A-Za-z0-9_]*$/ )
or not ( parts[1] ~ /^[A-Za-z_][A-Za-z0-9_]*$/ );
let tab := parts[0];
let col := parts[1];
let sql := sprint(
"SELECT 1 AS found FROM %s WHERE %s=?",
self._table_class()._sql_identifier(tab),
self._table_class()._sql_identifier(col),
);
let sth := self.get_schema().get_dbh().prepare(sql);
sth.execute(value);
die "Column " _ column{name} _ " refers to missing " _
column{exists_in} if not sth.next_typed_dict();
return true;
}
method insert () {
from std/string import join, sprint;
die "Cannot insert row already in database"
if self{in_database};
self._apply_defaults();
self._validate_required_columns_for_write();
let cols := self.get_column_metadata().map( function ( c ) {
return c{name};
} ).grep( function ( col ) {
return self{column_data}.exists(col);
} );
die "No column values to insert into " _ self.get_table_name()
if cols.length = 0;
self._validate_database_constraints(cols);
self._run_hooks("before_insert");
let placeholders := cols.map( function ( col ) {
return "?";
} );
let sql := sprint(
"INSERT INTO %s (%s) VALUES (%s)",
self._table_class()._sql_table(),
join( ", ", cols.map( function ( col ) {
return self._table_class()._sql_identifier(col);
} ) ),
join( ", ", placeholders ),
);
let values := cols.map( function ( col ) {
return self{column_data}{(col)};
} );
self.get_schema().get_dbh().prepare(sql).execute( ... values );
self{dirty} := {};
self{in_database} := true;
self._run_hooks("after_insert");
return self;
}
method update () {
from std/string import join, sprint;
die "Cannot update row not in database"
if not self{in_database};
self._validate_required_columns_for_write();
let pkey_names := self._table_class()._primary_key_names();
die "Cannot update table without primary key: " _
self.get_table_name()
if pkey_names.length = 0;
let pkey_lookup := {};
for ( let pkey_name in pkey_names ) {
pkey_lookup{(pkey_name)} := true;
}
let dirty_pkeys := pkey_names.grep( function ( col ) {
return self{dirty}.get( col, false );
} );
die "Cannot update dirty primary key fields: " _
join( ", ", dirty_pkeys )
if dirty_pkeys.length;
let cols := self.get_column_metadata().map( function ( c ) {
return c{name};
} ).grep( function ( col ) {
return self{dirty}.get( col, false )
and not pkey_lookup.exists(col);
} );
return self if cols.length = 0;
self._validate_database_constraints(cols);
self._run_hooks("before_update");
let assignments := cols.map( function ( col ) {
return self._table_class()._sql_identifier(col) _ "=?";
} );
let where := pkey_names.map( function ( col ) {
return self._table_class()._sql_identifier(col) _ "=?";
} );
let sql := sprint(
"UPDATE %s SET %s WHERE %s",
self._table_class()._sql_table(),
join( ", ", assignments ),
join( " AND ", where ),
);
let values := cols.map( function ( col ) {
return self{column_data}{(col)};
} );
for ( let pkey_name in pkey_names ) {
die "Missing primary key value for " _ pkey_name
if not self{column_data}.exists(pkey_name);
values.push( self{column_data}{(pkey_name)} );
}
self.get_schema().get_dbh().prepare(sql).execute( ... values );
self{dirty} := {};
self._run_hooks("after_update");
return self;
}
method delete () {
from std/string import join, sprint;
die "Cannot delete row not in database"
if not self{in_database};
let pkey_names := self._table_class()._primary_key_names();
die "Cannot delete from table without primary key: " _
self.get_table_name() if pkey_names.length = 0;
self._run_hooks("before_delete");
let where := pkey_names.map( function ( col ) {
return self._table_class()._sql_identifier(col) _ "=?";
} );
let sql := sprint(
"DELETE FROM %s WHERE %s",
self._table_class()._sql_table(),
join( " AND ", where ),
);
let values := [];
for ( let pkey_name in pkey_names ) {
die "Missing primary key value for " _ pkey_name
if not self{column_data}.exists(pkey_name);
values.push( self{column_data}{(pkey_name)} );
}
self.get_schema().get_dbh().prepare(sql).execute( ... values );
self{dirty} := {};
self{in_database} := false;
self._run_hooks("after_delete");
return self;
}
method _rq_find ( pkey ) {
const pkey_names := self._table_class()._primary_key_names();
let pkey_values := [];
if ( pkey instanceof Array ) {
let i := 0;
while ( i < pkey_names.length() ) {
pkey_values.push(
self._deflate_column( pkey_names[i], pkey[i] )
);
++i;
}
}
else if ( pkey instanceof Dict or pkey instanceof PairList ) {
for ( let pkey_name in pkey_names ) {
pkey_values.push(
self._deflate_column(
pkey_name,
pkey{(pkey_name)}
)
);
}
}
else if ( pkey_names.length = 1 ) {
pkey_values := [
self._deflate_column( pkey_names[0], pkey )
];
}
else {
die "Expected Array or Dict of primary key values";
}
from std/string import sprint, join;
let sql := sprint(
"SELECT * FROM %s WHERE %s",
self._table_class()._sql_table(),
join( " AND ", pkey_names.map( function ( col ) {
return self._table_class()._sql_identifier(col) _ "=?";
} ) ),
);
let sth := self.get_schema().get_dbh().prepare(sql);
sth.execute( ... pkey_values );
let row := sth.next_typed_dict();
if ( row ) {
return self._table_class()._from_database(row);
}
return null;
}
method _rq_search_column ( String col ) {
return self._column(col);
}
method _rq_search_deflate ( String col, value ) {
return self._deflate_column( col, value );
}
method _rq_search_operator ( String raw_op ) {
let op := uc raw_op;
return op if op eq "=";
return op if op eq "!=";
return op if op eq "<>";
return op if op eq "<";
return op if op eq "<=";
return op if op eq ">";
return op if op eq ">=";
return op if op eq "LIKE";
return op if op eq "NOT LIKE";
return op if op eq "ILIKE";
return op if op eq "IN";
return op if op eq "NOT IN";
return op if op eq "BETWEEN";
die "Unsupported search operator: " _ raw_op;
}
method _rq_search_compare ( String col, condition, Array binds ) {
let column := self._rq_search_column(col);
let sql_col := self._table_class()._sql_identifier(column{name});
let op := "=";
let value := condition;
if ( condition instanceof Array ) {
die "Search condition array for " _ col _
" needs [ operator, value ]"
if condition.length() ≢ 2;
op := self._rq_search_operator(condition[0]);
value := condition[1];
}
if ( op eq "IN" or op eq "NOT IN" ) {
die "Search " _ op _ " condition for " _ col _
" expects an Array"
if not ( value instanceof Array );
return op eq "IN" ? "0=1" : "1=1"
if value.length() = 0;
from std/string import join;
for ( let item in value ) {
binds.push( self._rq_search_deflate( col, item ) );
}
return sql_col _ " " _ op _ " (" _
join( ", ", value.map( → "?" ) ) _ ")";
}
if ( op eq "BETWEEN" ) {
die "Search BETWEEN condition for " _ col _
" expects [ low, high ]"
if not ( value instanceof Array )
or value.length() ≢ 2;
binds.push( self._rq_search_deflate( col, value[0] ) );
binds.push( self._rq_search_deflate( col, value[1] ) );
return sql_col _ " BETWEEN ? AND ?";
}
if ( value ≡ null ) {
return sql_col _ " IS NULL" if op eq "=";
return sql_col _ " IS NOT NULL" if op eq "!=" or op eq "<>";
}
binds.push( self._rq_search_deflate( col, value ) );
if ( op eq "ILIKE" ) {
return "LOWER(" _ sql_col _ ") LIKE LOWER(?)";
}
return sql_col _ " " _ op _ " ?";
}
method _rq_search_compile ( conditions, Array binds ) {
return "1=1" if conditions ≡ null;
if ( conditions instanceof Array ) {
return self._rq_search_compile_array( conditions, binds, "AND" );
}
if ( conditions instanceof Dict or conditions instanceof PairList ) {
return self._rq_search_compile_pairs( conditions, binds );
}
die "Search conditions must be Dict, PairList, or Array";
}
method _rq_search_compile_array (
Array conditions,
Array binds,
String op
) {
return "1=1" if conditions.length() = 0;
from std/string import join;
let parts := conditions.map( function ( item ) {
return self._rq_search_compile( item, binds );
} );
return "(" _ join( " " _ op _ " ", parts ) _ ")";
}
method _rq_search_compile_pairs ( conditions, Array binds ) {
let parts := [];
for ( let pair in conditions.to_Array() ) {
let key := pair.key;
let value := pair.value;
if ( key eq "AND" or key eq "OR" ) {
let group := value instanceof Array ? value : [ value ];
parts.push(
self._rq_search_compile_array( group, binds, key )
);
}
else if ( key eq "NOT" ) {
parts.push(
"NOT (" _ self._rq_search_compile( value, binds ) _ ")"
);
}
else if ( key eq "opts" ) {
next;
}
else {
parts.push( self._rq_search_compare( key, value, binds ) );
}
}
return "1=1" if parts.length() = 0;
from std/string import join;
return join( " AND ", parts );
}
method _rq_search_opts ( PairList conditions ) {
let opts := conditions.get( "opts", {} );
let clean := conditions.copy;
clean.remove("opts");
return { conditions: clean, opts: opts };
}
method _rq_search_order_sql ( opts ) {
return "" if not opts.exists("order_by");
let items := opts{order_by} instanceof Array
? opts{order_by}
: [ opts{order_by} ];
let parts := [];
for ( let item in items ) {
if ( item instanceof Array ) {
let direction := uc( item.length() > 1 ? item[1] : "ASC" );
die "Unsupported order direction: " _ direction
if direction ne "ASC" and direction ne "DESC";
parts.push(
self._table_class()._sql_column(item[0]) _ " " _ direction
);
}
else {
parts.push( self._table_class()._sql_column(item) _ " ASC" );
}
}
from std/string import join;
return parts.length() ? " ORDER BY " _ join( ", ", parts ) : "";
}
method _rq_search_limit_sql ( opts, Array binds ) {
let sql := "";
if ( opts.exists("limit") ) {
sql _= " LIMIT ?";
binds.push(opts{limit});
}
if ( opts.exists("offset") ) {
sql _= opts.exists("limit") ? " OFFSET ?" : " LIMIT -1 OFFSET ?";
binds.push(opts{offset});
}
return sql;
}
method _rq_search_run ( conditions, opts ) {
from std/string import sprint;
let binds := [];
let where := self._rq_search_compile( conditions, binds );
let sql := sprint(
"SELECT * FROM %s WHERE %s",
self._table_class()._sql_table(),
where,
) _ self._rq_search_order_sql(opts)
_ self._rq_search_limit_sql( opts, binds );
let sth := self.get_schema().get_dbh().prepare(sql);
sth.execute( ... binds );
let rows := [];
for ( let row in sth.all_typed_dict() ) {
rows.push( self._table_class()._from_database(row) );
}
return rows;
}
method _rq_search ( PairList conditions ) {
let parts := self._rq_search_opts(conditions);
return self._rq_search_run( parts{conditions}, parts{opts} );
}
method _rq_all ( PairList args ) {
return self._rq_search_run( {}, args.get( "opts", {} ) );
}
method _rq_first ( PairList conditions ) {
let parts := self._rq_search_opts(conditions);
let opts := parts{opts}.copy;
opts{limit} := 1;
let rows := self._rq_search_run( parts{conditions}, opts );
return rows.length() ? rows[0] : null;
}
method _rq_count ( PairList conditions ) {
let parts := self._rq_search_opts(conditions);
return self._rq_count_run(parts{conditions});
}
method _rq_exists ( PairList conditions ) {
return self._rq_count(conditions) > 0;
}
method _rq_count_run ( conditions ) {
from std/string import sprint;
let binds := [];
let where := self._rq_search_compile( conditions, binds );
let sql := sprint(
"SELECT COUNT(*) AS rowquill_count FROM %s WHERE %s",
self._table_class()._sql_table(),
where,
);
let sth := self.get_schema().get_dbh().prepare(sql);
sth.execute( ... binds );
return sth.next_typed_dict(){rowquill_count};
}
method _rq_find_or_create ( PairList opts ) {
let rows := self._rq_search_run( opts{find}, { limit: 1 } );
return rows[0] if rows.length();
let data := {};
for ( let pair in opts{find}.to_Array() ) {
next if pair.key eq "AND" or pair.key eq "OR" or pair.key eq "NOT";
next if pair.value instanceof Array;
data{(pair.key)} := pair.value;
}
for ( let pair in opts.get( "create", {} ).to_Array() ) {
data{(pair.key)} := pair.value;
}
let row := self._table_class()._create_from(data);
row.insert();
return row;
}
method _rq_create_or_update ( PairList opts ) {
let rows := self._rq_search_run( opts{find}, { limit: 1 } );
let row := rows.length()
? rows[0]
: self._table_class()._create_from(opts{find});
for ( let pair in opts.get( "set", {} ).to_Array() ) {
row._set_column( pair.key, pair.value );
}
row.insert_or_update();
return row;
}
method is_dirty () {
return self{dirty}.to_Array().length() > 0;
}
method dirty_fields () {
return self{dirty}.to_Array().grep(
fn pair → pair.value
).map(
fn pair → pair.key
);
}
method mark_clean () {
self{dirty} := {};
return self;
}
method reload () {
die "Cannot reload row not in database"
if not self{in_database};
let fresh := self._table_class().find( self._primary_key_value() );
die "Row no longer exists in database" if fresh ≡ null;
self{column_data} := fresh{column_data}.copy;
self{dirty} := {};
self{in_database} := true;
return self;
}
method _primary_key_value () {
let pkeys := self._table_class()._primary_key_names();
die "Cannot use row without primary key"
if pkeys.length = 0;
if ( pkeys.length = 1 ) {
die "Missing primary key value for " _ pkeys[0]
if not self{column_data}.exists(pkeys[0]);
return self._get_column(pkeys[0]);
}
let values := [];
for ( let pkey in pkeys ) {
die "Missing primary key value for " _ pkey
if not self{column_data}.exists(pkey);
values.push( self._get_column(pkey) );
}
return values;
}
method insert_or_update () {
try { self.insert() } catch (e) { self.update() };
}
}
class ClassMaker {
let table;
let schema;
let _cols := [];
let _pkey := [];
let _rels := [];
let _helpers := [];
let _hooks := {
before_insert: [],
after_insert: [],
before_update: [],
after_update: [],
before_delete: [],
after_delete: [],
};
method add_column ( String colname, String type, ... PairList pl ) {
const opts := pl ? pl.copy : {{}};
opts{name} := colname;
opts{type} := type;
_cols.push( opts );
_pkey.push( opts ) if opts{primary};
}
method add_helper ( String methodname, Function cb, ... PairList pl ) {
const opts := pl ? pl.copy : {{}};
opts{name} := methodname;
opts{callback} := cb;
_helpers.push( opts );
}
method add_static ( String methodname, Function cb, ... PairList pl ) {
const opts := pl ? pl.copy : {{}};
opts{name} := methodname;
opts{callback} := cb;
opts{is_static} := true;
_helpers.push( opts );
}
method before_insert ( Function cb ) { _hooks{before_insert}.push(cb); }
method after_insert ( Function cb ) { _hooks{after_insert}.push(cb); }
method before_update ( Function cb ) { _hooks{before_update}.push(cb); }
method after_update ( Function cb ) { _hooks{after_update}.push(cb); }
method before_delete ( Function cb ) { _hooks{before_delete}.push(cb); }
method after_delete ( Function cb ) { _hooks{after_delete}.push(cb); }
method has_one ( ... PairList pl ) {
const opts := pl.copy;
self._validate_relationship_opts(opts);
opts{type} := "has_one";
_rels.push(opts);
}
method has_many ( ... PairList pl ) {
const opts := pl.copy;
self._validate_relationship_opts(opts);
opts{type} := "has_many";
_rels.push(opts);
}
method _validate_relationship_opts ( opts ) {
let join := opts{join};
die "Relationship join must be a Dict or PairList"
if not ( join instanceof Dict or join instanceof PairList );
die "Relationship where must be a Dict or PairList"
if opts.exists("where")
and not (
opts{where} instanceof Dict
or opts{where} instanceof PairList
);
return true;
}
method _make_accessors () {
from std/string import join;
return join( "\n", self{_cols}.map( function (c) {
const colname := c{name};
const accessorname := c{accessor} ?: c{name};
let code := ```
method ${accessorname} ( ... PairList opts ) {
if ( opts.exists("set") ) {
self._set_column(
"${colname}",
opts{set},
opts.get( "raw", false )
);
}
return self._get_column(
"${colname}",
opts.get( "raw", false )
);
}
```;
if ( accessorname ne colname ) {
code _= ```
method ${colname} ( ... PairList opts ) {
return self.${accessorname}( ... opts );
}
```;
}
return code;
} ) );
}
method _make_relationship_accessors () {
from std/string import join;
let i := -1;
return join( "\n", self{_rels}.map( function (r) {
i++;
const accessorname := r{accessor};
if ( r{type} eq "has_one" ) {
return ```
method ${accessorname} ( ... PairList opts ) {
return self._set_relationship(
self.get_relationship_metadata()[${i}],
opts{set}
) if opts.exists("set");
return self._has_one(
self.get_relationship_metadata()[${i}]
);
}
```;
}
return ```
method ${accessorname} ( ... PairList opts ) {
return self._set_relationship(
self.get_relationship_metadata()[${i}],
opts{set}
) if opts.exists("set");
return self._has_many(
self.get_relationship_metadata()[${i}]
);
}
```;
} ) );
}
method _make_helpers () {
from std/string import join;
let i := -1;
return join( "\n", self{_helpers}.map( function (h) {
i++;
let metadata := h{is_static}
? "self._helper_metadata()"
: "self.get_helper_metadata()";
return ```
${ h{is_static} ? "static method" : "method" } ${ h{name} } ( ... Array a, PairList p ) {
return ${metadata}[${i}]{callback}( self, ...a, ...p );
}
```
} ) );
}
method make_class () {
const CODE := do {
from std/string import camel;
let class_name := camel( self{table} );
class_name[0] := "TABLE_" _ uc class_name[0];
```
let _cached_pkeys;
class ${class_name} with TableClass {
let column_data := {};
let dirty := {};
let in_database := false;
method get_schema () {
return SCHEMA;
}
method get_table_name () {
return TABLE_NAME;
}
method get_column_metadata () {
return COLUMNS;
}
method get_relationship_metadata () {
return RELATIONSHIPS;
}
method get_helper_metadata () {
return HELPERS;
}
method get_hook_metadata () {
return HOOKS;
}
static method _schema () {
return SCHEMA;
}
static method _table_name () {
return TABLE_NAME;
}
static method _column_metadata () {
return COLUMNS;
}
static method _helper_metadata () {
return HELPERS;
}
static method _sql_identifier ( String name ) {
die "Invalid SQL identifier: " _ name
if not ( name ~ /^[A-Za-z_][A-Za-z0-9_]*$/ );
return "\"" _ name _ "\"";
}
static method _column_def ( String col ) {
for ( let c in self._column_metadata() ) {
return c if c{name} eq col;
return c if c.get( "accessor", "" ) eq col;
}
die "No such column: " _ col;
}
static method _sql_column ( String col ) {
return self._sql_identifier( self._column_def(col){name} );
}
static method _sql_table () {
return self._sql_identifier( self._table_name() );
}
static method _primary_key_names () {
_cached_pkeys ?:= self._column_metadata().grep(
→ ^^.get( "primary", false )
);
return _cached_pkeys.map( → ^^{name} );
}
static method create ( ... PairList opts ) {
return self._create_from(opts);
}
static method _create_from ( data ) {
let i := new self();
for ( data.to_Array() ) {
i._set_column( ^^.key, ^^.value );
}
i._apply_defaults();
return i;
}
static method _from_database ( Dict row ) {
let i := new self();
for ( let pair in row.to_Array() ) {
i{column_data}{(pair.key)} := pair.value;
}
i{dirty} := {};
i{in_database} := true;
return i;
}
static method find ( pkey ) {
return ( new self() )._rq_find(pkey);
}
static method _search_run ( conditions, opts ) {
return ( new self() )._rq_search_run( conditions, opts );
}
static method search ( ... PairList conditions ) {
return ( new self() )._rq_search(conditions);
}
static method all ( ... PairList args ) {
return ( new self() )._rq_all(args);
}
static method first ( ... PairList conditions ) {
return ( new self() )._rq_first(conditions);
}
static method count ( ... PairList conditions ) {
return ( new self() )._rq_count(conditions);
}
static method exists ( ... PairList conditions ) {
return ( new self() )._rq_exists(conditions);
}
static method _count_run ( conditions ) {
return ( new self() )._rq_count_run(conditions);
}
static method find_or_create ( ... PairList opts ) {
return ( new self() )._rq_find_or_create(opts);
}
static method create_or_update ( ... PairList opts ) {
return ( new self() )._rq_create_or_update(opts);
}
${self._make_accessors()}
${self._make_relationship_accessors()}
${self._make_helpers()}
}
${class_name};
```
};
from std/eval import eval;
const SCHEMA := self{schema};
const TABLE_NAME := self{table};
const COLUMNS := self{_cols};
const RELATIONSHIPS := self{_rels};
const HELPERS := self{_helpers};
const HOOKS := self{_hooks};
return eval( CODE );
}
}
modules/db/rowquill/tableclass.zzm
rowquill-0.0.1 source code
Package
- Name
- rowquill
- Version
- 0.0.1
- Uploaded
- 2026-06-14 10:29:16
- Repository
- https://github.com/tobyink/zuzu-rowquill
- Dependencies
-
-
std/db>= 0 -
std/eval>= 0 -
std/string>= 0
-
- Metadata
- zuzu-distribution.json
- Archive
- Download .tar.gz