Data Def™️

Data Def™️ is a specification for declaratively indicating how blocks of data are utilized.

Data Def Fields

The following section outlines a comprehensive list of currently supported Data Def fields.

Identity Fields

id

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 {
  ...
}

namespace

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.

User Interface Fields

displayName

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.

description

type: string\ default: undefined

A brief description of the property. The description is used in tooltips, detail sections, and documentation.

hint

type: string\ default: undefined

A description of the property to be displaying in user interface hint areas.

type

type: string\ default: undefined

The type of data. This can be inferred from the default value or the schema.

type<2..n>

type: string\ default: undefined

The subtype of data.

For example

{
  type: string;
  type2: password;
}

State Management Fields

Value fields affect how a value is loaded or saved

provider

type: string\ default: undefined

Specifies the resource provider that manages the data.

provider-props

type: object\ default: undefined

Provider specific properties

scope

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

persist

Indicates how data is stored and loaded between instantiations

default

type: any | function\ default: undefined

The default value of the data. This value will be used in several circumstances:

  • to initialize a provider
  • when resetting the data
  • when a provider cannot be reached
  • to determine if a value is not the default
  • generating a schema

validate

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.

bind

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.

schema

type: object\ default: undefined

Determines the schema to use for a property

primary

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
}

topology

type: event | group | user\ default: undefined

Determines how the value is distributed and persisted.

loading

type: TBD\ default: undefined

Determines how the value is handled during loading and errors.

Security Fields

enumerable

type: boolean\ default: undefined

Indicates if the field should be enumerated in user interfaces

secure

type: object\ default: undefined

Indicates if the field should be encrypted when storing or transmitting

Data Shaping Fields

shaping

type: object\ default: undefined

The following fields may be set in the shaping object. They are all optional.

commit

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

debounce

type: number

Delays sending updates until time based conditions are met.

throttle

type: number

Limits the frequency of updates sent. Throttling does not alter content.

immediate

type: boolean

Indicates if the value is sent immediately, before and timeouts.

threshold

type: number

Indicates the change between 2 set values required to trigger an immediate send.

compress

Indicates if, when, and how data is compressed before transfering or storing. Also indicates compression parameters and protocol.

accumulate

Indicates if and how cached updates are combined or replaced before sending them.

Developer Tools Fields

debug

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.

log

type: true | false | (read | write | permission|error)[]\ default: undefined

Indicates if data access (reads, writes, permissions, errors) of the data should be logged.

Unfiled Fields

filter

Data Language Block

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 Management

Data Block can automatically manage many application concerns. Here is a comprehensive list.

[todo descriptions and categories]

  • Availability
  • Structure
  • Schema Generation
  • Validation
    • manual validation
    • automatic validation
    • phases
    • field validation
    • block validation
    • local validation
    • remote validation
  • Searchable Options
  • Field Mapping
  • Initialization
  • External Change Notification
  • Persistence
  • Error Handling
  • Synchronization
  • Exclusivity And Locks
  • Sharing
  • Shaping
    • throttling
    • debouncing
    • aggregation
    • summation
    • averaging
    • merging
    • filtering
  • Binding (To UI and other data sources)
  • Security
  • Plugin / Tapping
  • Monitoring
  • Identification / Organization
  • Reusability
  • UI Generation
  • Conflict Resolution
  • Undo/Redo
  • Loading State
  • Error State
  • Transactions
  • Dirty state
    • detect if the local and remote state are out of sync
  • SAS (Selective Aspect Synchronization)
  • Hysteresis
  • Reset
  • Creation factories
  • Memory and object tracking
  • i8n - internationalization for language strings
  • Documentation
  • Context sensitive help
  • Composable definitions
  • Language independent specifications
  • Field Mapping
    • Field Flattening
  • Enumerable
  • Security
    • Encryption
    • Transmission
    • Storage
    • Visibility
    • Salt

The <data> block

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>

Data Block Syntax

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;}

DataId and Field Names

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

Field Values

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

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;
}

Examples

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>

Preprocessing Existing Source Code

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>

Data Def Fields

See Data Def Fields


Data Block Object

A data block can also be created directly.

Data Block declaration using Javascript

const defs = {
  name: {
    default: User;
  },
  age: {
    :default: null;
  }
}

Data Block API

The Data Block API provides functionality to manage data, data definition fields, and interactive services.

Standard API methods

get

Retrieve the data associated with a data definition

set

Set the data associated with a data definition

getDefinition

Retrieve a data definition

getDefinitions

Retrieve all data definitions

getFields

Retrive a data definition fields (not the realized field value)

getField

Retrive a data definition field (not the realized field value)

getFieldValue

watch

getRef (todo)

bind

autobind

Standard API Messages

dataBlockParsed

dataBlockBound

Syntactic Sugar

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>

<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>

<template>
  <div ^description /> <!-- v-model="getDataRef('description')" -->
  <div ^^description /> <!-- v-model="getDataValue('description')" -->
</template>

<Style>

<style>
.some-class::before {
  content: ^description
}
.another-class::before {
  url(^some-data-source)
}
</style>