Data Def™️ is a specification for declaratively indicating how blocks of data are utilized.
The following section outlines a comprehensive list of currently supported Data Def fields.
type: string
A unique identifier used to reference the definition. This identifier must be unique within the data block it is defined in.
An id must conform to the regular expression [a-zA-Z_][a-zA-Z0-9_-]*
or the reserved id *
* {
...
}
dataId-1 {
...
}
dataId-n {
...
}
type: string\ default: undefined
An optional namespace to use. Namespaces are used to resolve ids within multiple data block instances and declarations. Namespaces may be used by a provider to partition data regions.
type: string\ default: undefined
A human readable name used in reports and user interfaces. A variant of the id will be used if not specified.
type: string\ default: undefined
A brief description of the property. The description is used in tooltips, detail sections, and documentation.
type: string\ default: undefined
A description of the property to be displaying in user interface hint areas.
type: string\ default: undefined
The type of data. This can be inferred from the default value or the schema.
type: string\ default: undefined
The subtype of data.
For example
{
type: string;
type2: password;
}
Value fields affect how a value is loaded or saved
type: string\ default: undefined
Specifies the resource provider that manages the data.
type: object\ default: undefined
Provider specific properties
type: string \ default: undefined
Specifies the accounts, applications, sessions, and devices that share the data. Scope is meant to augment provider settings with universal parameters. See [[Data Language Block Scopes]]
Scope does not define persistence
Indicates how data is stored and loaded between instantiations
type: any | function\ default: undefined
The default value of the data. This value will be used in several circumstances:
type: function\ default: undefined
A function to validate data integrity when initializing it from an external source. This function can modify the incoming value or properties on the incoming value.
The signature the function is:
(value:T, context:Context) => T | undefined
A validate function can return a new or replaced value. It can also throw an exception.
If the validate function returns undefined, the value is considered valid.
type: selector-string | element | (selector-string | element)[] | function\ default: undefined
Targets to use when autobinding data to elements. Multiple targets may be set for the same dataId.
type: object\ default: undefined
Determines the schema to use for a property
type: true | false | number\ default: undefined
Determines the exclusivity of a field. This may be configured to allow a field to be modified by only a single peer over a period of time. For instance, the system may be configured to a single peer to scroll, with a mandatory timeout before allowing another peer to scroll.
{
type: 'none' | 'default' | 'json'
value: any
}
type: event | group | user\ default: undefined
Determines how the value is distributed and persisted.
type: TBD\ default: undefined
Determines how the value is handled during loading and errors.
type: boolean\ default: undefined
Indicates if the field should be enumerated in user interfaces
type: object\ default: undefined
Indicates if the field should be encrypted when storing or transmitting
type: object\ default: undefined
The following fields may be set in the shaping object. They are all optional.
type: boolean
Only persist the value when commit
is called. This operation may be attached to an element blur (losing focus), to an enter
keypress on a text field, user interface side shaping, or by pressing a commit
button, etc.
When commit is enabled, no other shaping parameters are considered.1
type: number
Delays sending updates until time based conditions are met.
type: number
Limits the frequency of updates sent. Throttling does not alter content.
type: boolean
Indicates if the value is sent immediately, before and timeouts.
type: number
Indicates the change between 2 set values required to trigger an immediate
send.
Indicates if, when, and how data is compressed before transfering or storing. Also indicates compression parameters and protocol.
Indicates if and how cached updates are combined or replaced before sending them.
type: true | false\ default: undefined
Indicates if the data should be debugged. This includes tracing operations such as lifecycle, initialization, validation, reads, writes, and errors.
type: true | false | (read | write | permission|error)[]\ default: undefined
Indicates if data access (reads, writes, permissions, errors) of the data should be logged.
URM uses a declarative approach for working with data.
URM defines a data language block specification that allows the platform to manage boilerplate functionality The language block also allows developers to manage the granularity of data functionality from a centralized location. The declarative approach also facilitates code reuse.
Data can be defined using many data block fields that configure features such as persistence, groups, network shaping, debugging, etc. Data definitions may be referenced using their ID from multiple places within a codebase.
The Data Block API provides methods to interact with data definitions, the client, and providers.
Data Language Block features can be leveraged by simply creating data block value from a data block definition or a plain object.
const animal = {
name: 'Woolly',
type: 'dog'
}
// Equivalent conversions
const animal = dlb({
name: 'Woolly',
type: 'dog'
})
const animal = dlb({
name: ()=> 'Woolly',
type: ()=> 'dog',
})
const animal = dlb({
name: { default: ()=> 'Woolly', displayName: 'Name' },
type: { default:()=> 'dog', displayName: 'Type' },
})
// Default field values can be set for all fields.
const animal = dlb({
name: 'Woolly',
type: 'dog'
}, {provider:’local.shared’})
Data Block can automatically manage many application concerns. Here is a comprehensive list.
[todo descriptions and categories]
The current approach used by modern web technologies is to combine three types of language blocks into one or more files. This structure follows the "separation of concerns" principle. For an example, see https://vuejs.org/guide/scaling-up/sfc.html.
<template>
<!-- structure and content (typically HTML) -->
</template>
<script>
<!-- logic, executable code and data declarations (typically Javascript) -->
</script>
<style>
<!-- style directives (typically CSS) -->
</style>
URM adds an additional Data Language Block
type to the standard language blocks.
<data>
<!-- data binding, persistence, shaping, flow control, validation, provider, ui hints, etc. -->
</data>
The syntax of a data block resembles CSS and Javascript. Whereas CSS defines selectors
, a data block defines data block definitions
. Each data block definition
is comprised of a dataId
, followed by data block definition fields
.
<dataId> {
<field-name>: <field-value>;
<field-name-n>: <field-value-n>;
}
<dataId-n> {
<field-name>: <field-value>;
<field-name-n>: <field-value-n>;
}
Whitespace is optional.
name{displayName:Name;description:Your full name;}age{type:number;}
This regular expression describes valid id and field names: [a-zA-Z_][a-zA-Z_0-9\-]*
Examples of valid values
a
_a
ab
a-b
a_b
a8
a_
a-
Examples of invalid values
-a
-8
8a
hello.world
A field value is always interpreted as a string, with any surrounding whitespace trimmed.
If the field name is prefixed with a :
, the field value will be evaluated using either eval(...)
or JSON.parse(...)
. If the evaluated value returns a function
, the function will invoked to resolve the actual value.
name_eval {
displayName: () => "Some" + " Person"
}
name_json {
displayName: "Some Person"
}
Field values terminate with a ;
. If a field value uses a ;
or \
character, the character must be escaped with a \
character. This applies to strings, parsed strings, and evaluated strings
name {
description: I have a \; and a \\ in my description;
}
Default field values may be defined using the reserved DataId *
. These field values apply to any definition in the Data Block that omits the field.
* {
provider: some_provider;
namespace: some_namespace;
}
An example Data Block with options.
<data>
* {
provider: LocalStorage;
}
my-string {
scope: workspace;
:debounce: 100;
:validate: myValidateFunction;
bind: #some-element
default: () => {message: 'Hello World'}
}
</data>
<script>
// get the provided value
const myString = await dataBlock.get('my-string')
// 2-way bind
await dataBlock.bind('my-string', '#my-element')
// autobind definitions that use the bind field
await dataBlock.autobind();
</script>
Until the data block becomes a web standard, the data block content can be added via a script. Furthermore, an HTML preprocessor can replace the data block with a script.
<html>
<head>
<script>
window.urm_data_block_string = `
my-text-area {
scope: workspace;
debounce: 100;
validate: myValidateFunction;
default: () => {message: 'Hello World'}
}
...
`
window.urm_data_block = parseDataBlock(wi dow.urm_data_block_string)
</script>
<script
</head>
</html>
See Data Def Fields
A data block can also be created directly.
Data Block declaration using Javascript
const defs = {
name: {
default: User;
},
age: {
:default: null;
}
}
The Data Block API provides functionality to manage data, data definition fields, and interactive services.
Retrieve the data associated with a data definition
Set the data associated with a data definition
Retrieve a data definition
Retrieve all data definitions
Retrive a data definition fields (not the realized field value)
Retrive a data definition field (not the realized field value)
To allow for terse code, several shortcut macros have been added to the other three language block sections. As with the data language block, a preprocessor can replace the shortcut macros with valid code.
<script>
const myTextArea = ^description // getDataRef('description')
const myTextArea = ^^description // getDataValue('description')
^description = 'Some description' // setDataValue('description', 'Some description')
const debounce = ^description.debounce // getDataProperty('description', 'debounce')
const debounce = ^description.* // getDataProperties('description')
const defaultValue = ^description?.default() // getDataProperty('description', 'default')?.()
</script>
<template>
<div ^description /> <!-- v-model="getDataRef('description')" -->
<div ^^description /> <!-- v-model="getDataValue('description')" -->
</template>
<style>
.some-class::before {
content: ^description
}
.another-class::before {
url(^some-data-source)
}
</style>