-
Notifications
You must be signed in to change notification settings - Fork 5
Active Row
activerow
is a hybrid implementation of the Active Record design pattern. Unlike ds_row
which typically represents an existing row, activerow
may not yet exist as a row in the data source. It can either be used stand alone or as the parent for your own types — it expands on the functionality of ds_row
.
Rows returned from ds
can be cast as activerow
types (or other types based on it) like so:
with row #ds->rows(::activerow) do {
#row->isa(::activerow) // true
}
Or:
with row in #ds->rows(::product) do { // product inherits from activerow
#row->isa(::activerow) // true
}
This allows you to work with your own type definitions in an OOP fashion with minimal overhead and is one of the key strengths of the Datasource suite of tools.
You can also create a new row / object like so:
local(product) = activerow(::store.product)
#product->save(
::brand = 'ACME',
::description = 'Example Product'
::price = 9.95
)
Finally you can specify a memberstream
when returning rows — although you will need to specify an ->oncreate
method that accepts ds_row
as the first parameter:
with product in result->rows(\product) do {
#product->isa(::activerow) // true
}
The activerow
type can be created with the following creators:
-> oncreate(database.table::tag,...optionalkeys)
-> oncreate(ds::ds,...optionalkeys)
-> oncreate(row::ds_row)
-> oncreate(keyvalue::integer)
-> oncreate(keyvalue::string)
-> oncreate(keyvalues::pair,...morepairs)
Alternatively you can specific the database and table with the oncreate
methods like so:
local(product) = activerow(::store.product,#id)
Or:
local(product) = activerow(::store.product,::uuid = #uuid)
Or:
local(product) = activerow(product_ds,::uuid = #uuid)
Within your own types you can also leverage the oncreate methods:
define product => type {
parent activerow
public oncreate(id::integer=0) => ..oncreate(::store.product,#id)
}
Or:
define product => type {
parent activerow
public oncreate(...keys) => ..oncreate(::store.product,#keys)
}
Or using a ds
definition:
define product => type {
parent activerow
public oncreate(mpn::string) => ..oncreate(product_ds,::mpn = #mpn)
}
And finally a complete catch all:
define product => type {
parent activerow
public oncreate(...) => ..oncreate(:#rest)
}
If you prefer you can configure you sub-types by providing either the database name or ds
definition via .database
and .ds
respectively. When using either approach you can also specify the table with .table
— by default a pluralised version of your type will be used for the table name.
define product => type {
parent activerow
public ds => ds(::store.product)
}
Or:
define product => type {
parent activerow
data
public database = 'store',
public table = 'products'
}
When .table
is blank activerow will use the types name suffixed with "s" as the table — you can change this with the following code:
define activerow_pluralise_tables => false
Data can be also be loaded into an activerow object by using the ->getrow
method. This method retrieves row data from the datasource and returns a reference tho the activerow
.
local(product) = activerow(::store.product)->getrow(::id = 123)
Data can be accessed in the same fashion as ds_row
with the added benefit of being able leverage your own methods defined in your types. This allows you to format the data in a particular way or create another accessor for the data.
Return the value of the specified column:
(column::tag)
-> find(column::tag)
-> find(column::string)
# activerow(::mycolumn)
# activerow->find('mycolumn')
When used in your own types you can create one to one methods like so:
public mycolumn => .find(::mycolumn)
Or you can use methods with different names like so:
public firstname => .find(::user_firstname)
public lastname => .find('user_lastname') // also valid
Bringing it all together:
public qty => .find(::item_qty)
public price => .find(::item_price)
public subtotal => .price * .qty
Updates internal data — does not write to data source.
# activerow(::mycol) = 'Value'
# activerow('mycol') = 'Value'
-> updatedata(data::trait_foreachpair)
-> updatedata(p::pair,...)
Updates internal data only — does not write to data source.
# activerow->updatedata(map('price' = 9.95, 'qty' = 3))
-> update(data::trait_foreachpair)
-> update(p::pair,...)
Updates internal data and writes to data source.
# activerow->update(::price = 9.95, ::qty = 3)
// Or
# activerow->update(
map(
'price' = 9.95,
'qty' = 3
)
)
-> set(p::pair)
-> set(column::tag) = value
-> set(column::string) = value
Update internal data and writes specified value to the data source.
// These all have the same effect
# activerow->set(::mycol) = 'Value'
# activerow->set('mycol') = 'Value'
// Supplied as a pair
# activerow->set(::mycol = 'Value')
# activerow->set('mycol' = 'Value')
Creates new row in the data source and assigns the newly created row to the type.
Either updates or creates a row depending if it's new or not (based on keyvalue).
Clears row data and deletes the row from the data source.
Reverts unsaved changes.
Returns true if new.
Returns copy of the activerow
with a null key value (considered new).
You can change the default behaviour of the above by redefining them in your type. For example you may want to perform additional tasks on save or disable deleting by replacing it with an update.
define example => type {
parent activerow
// Extend default save method
public save => {
// Do something else
log(.type + ' saved by ' + current_user->name)
// Now actually save
..save
}
// Override default delete method
public delete => .update(::status = -1)
}