Skip to content

Commit 193ee51

Browse files
author
Vlad Balin
committed
New site
1 parent d7aea20 commit 193ee51

File tree

114 files changed

+7613
-12630
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

114 files changed

+7613
-12630
lines changed

docs/00_Getting_Started.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Introduction
2+
3+
Type-R is the universal state management framework for both UI and domain state.
4+
5+
Application state is described with the superposition of two main container types - [Record](04_Record/00_Overview.md) and [Collection](./05_Collection/00_Overview.md) of records. The state has observable changes and is serializable by default.
6+
7+
- Records are classes with typed attributes. They are protected from attributes assignment of incompatible types with run-time type assertions and conversions. Which means that the client-server protocol is protected from both ends.
8+
- Records distinguish [aggregated attributes](06_API_by_feature/01_Aggregation_tree.md) and those which are [references to shared objects](06_API_by_feature/04_Shared_objects.md). Aggregated attributes are serialized as nested JSON, while references might be [serialized as ids](06_API_by_feature/05_id-references_and_Stores.md). Operations like `clone()`, `dispose()`, and `toJSON()` are performed recursively on elements of aggregation tree.
9+
- Type-R features declarative [validation](06_API_by_feature/03_Validation.md) with attribute-level rules. Validation is transparent and lazily evaluated.
10+
- Architecture does not depend on stores and singletons. [Stores](05_API_by_feature/05_id-references_and_Stores.md) are optional and used for shared data only. There might be as many stores as you need, and they can be created and disposed dynamically.
11+
- Type-R data structures are pretty efficient. They are about 10 times faster in real-world data scenarios than BackboneJS.
12+
13+
# Installation
14+
15+
Is packed as UMD and ES6 module. No side dependencies.
16+
17+
`npm install type-r --save-dev`
18+
19+
## Requirements
20+
21+
IE10+, Edge, Safari, Chrome, and Firefox are supported.
22+
23+
IE9 and Opera may work but has not been tested. IE8 _won't work_.
24+
25+
# FAQ
26+
## How the Type-R compares with X?
27+
28+
Type-R is designed to be the substitution for BackboneJS, which was used extensively 3 years ago in Volicon/Verizon Observer products. While it largely backward compatible by its API with BackboneJS (for Events and Collections), it's entirely different internally.
29+
30+
In its core, it's an engine for managing the _aggregation trees_ composed of nested Records and Collections. It contains no View, Router, REST, and jQuery/Underscore dependencies.
31+
32+
Feature | Type-R | BackboneJS | EmberJS | mobx
33+
-|-|-|-|-
34+
View and Router | - | ✓ | ✓ | - |
35+
Models | ✓ | ✓ | ✓ | Objects as models
36+
Collections | ✓ | ✓ | modeled as relations | Arrays as collections
37+
Nested Data Strictures | as aggregation trees and relations | - | as relations | as object graph
38+
Relations by id | resolved agains the chain of dynamic stores | - | resolved agains the global store | -
39+
JSON Serialization | ✓ | ✓ | ✓ | -
40+
Validation | ✓ | ✓ | ✓ | -

docs/04_Record/00_Overview.md

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
Record is the serializable class with typed attributes, observable changes, and custom validation checks. It is the main building block for managing the application state; component local state, stores, and collection elements are all subclasses of the `Record`.
2+
3+
In contrast to the "model" class in the majority of data frameworks, Record is *not the key-value hash*. It's the class with statically defined set of attributes of known types.
4+
5+
`Record` itself is an abstract class. The subclass needs to be defined for every data structure of different shape, in a similar way as it's done in statically typed languages.
6+
7+
```javascript
8+
import { define, Record } from 'type-r'
9+
10+
// ⤹ required to make magic work
11+
@define class User extends Record {
12+
// ⤹ attribute's declaration
13+
static attributes = {
14+
firstName : '', // ⟵ String type is inferred from the default value
15+
lastName : String, // ⟵ Or you can just mention its constructor
16+
email : String.value( null ), //⟵ Or you can provide both
17+
createdAt : Date, // ⟵ And it works for any constructor.
18+
// And you can attach ⤹ metadata to fine-tune attribute's behavior
19+
lastLogin : Date.value( null ).has.toJSON( false ) // ⟵ not serializable
20+
}
21+
}
22+
23+
const user = new User();
24+
console.log( user.createdAt ); // ⟵ this is an instance of Date created for you.
25+
26+
const users = new User.Collection(); // ⟵ Collections are defined automatically.
27+
users.on( 'changes', () => updateUI( users ) ); // ⟵ listen to the changes.
28+
29+
users.set( json, { parse : true } ); // ⟵ parse raw JSON from the server.
30+
users.updateEach( user => user.firstName = '' ); // ⟵ bulk update triggering 'changes' once
31+
```
32+
33+
# Record definition
34+
35+
Record definition must:
36+
37+
- be the class extending the `Record`;
38+
- be preceded with the `@define` decorator;
39+
- have `static attributes` definition.
40+
41+
### `decorator` @define
42+
43+
_Must_ be placed before record class definition.
44+
45+
```javascript
46+
import { define, Record } from 'type-r'
47+
48+
@define class X extends Record {
49+
...
50+
}
51+
```
52+
53+
### `static` attributes = { name : `attrDef`, ... }
54+
55+
Record's attributes definition. Lists attribute names along with their types, default values, and metadata controlling different aspects of attribute behavior.
56+
57+
```javascript
58+
@define class User extends Record {
59+
static attributes = {
60+
name : String.value( 'John Dow' ),
61+
email : '[email protected]', // Same as String.value( '[email protected]' )
62+
address : String, // Same as String.value( '' )
63+
}
64+
}
65+
```
66+
67+
The Record guarantee that _every attribute will retain the value of the declared type_. Whenever an attribute is being assigned with the value which is not compatible with its declared type, the type is being converted with an invocation of the constructor: `new Type( value )` (primitive types are treated specially).
68+
69+
## Attribute definitions
70+
71+
### `attrDef` name : Type
72+
73+
When the function is used as `attrDef`, it's treated as the constructor function. Any constructor function which behaves as _converting constructor_ (like `new Date( msecs )`) may be used as an attribute type.
74+
75+
You can use other record's and collection's constructors as attribute types. They will be treated as an _integral part_ of the record (created, serialized, validated, and disposed together), i.e. as _aggregated members_.
76+
77+
```javascript
78+
@define class Person extends Record {
79+
static attributes = {
80+
name : String // String attribute which is "" by default.
81+
createdAt : Date // Date attribute
82+
...
83+
}
84+
}
85+
```
86+
87+
### `attrDef` name : defaultValue
88+
89+
When value of other type than function is used as `attrDef` it's treated as attribute's default value. Attribute's type is being inferred from the value.
90+
91+
Use the general form of attribute definition for attributes of `Function` type: `Function.value( theFunction )`.
92+
93+
```javascript
94+
@define class GridColumn extends Record {
95+
static attributes = {
96+
name : '', // String attribute which is '' by default.
97+
render : Function.value( x => x ),
98+
...
99+
}
100+
}
101+
```
102+
103+
### `attrDef` name : Type.value( defaultValue )
104+
105+
The general form of attribute definition is `Type.value( defaultValue )`, where the `Type` is the corresponding constructor function.
106+
107+
```javascript
108+
@define class Person extends Record {
109+
static attributes = {
110+
phone : String.value( null ) // String attribute which is null by default.
111+
...
112+
}
113+
}
114+
```
115+
116+
# Create the record
117+
118+
Record behaves as regular ES6 class with attributes accessible as properties.
119+
120+
### new Record()
121+
122+
Create an instance of the record with default attribute values taken from the attributes definition.
123+
124+
When no default value is explicitly provided for an attribute, it's initialized as `new Type()` (just `Type()` for primitives). When the default value is provided and it's not compatible with the attribute type, it's converted with `new Type( defaultValue )` call.
125+
126+
### new Record({ attrName : value, ... }, options? )
127+
128+
When creating an instance of a record, you can pass in the initial attribute values to override the defaults.
129+
130+
If `{parse: true}` option is used, `attrs` is assumed to be the JSON.
131+
132+
If the value of the particular attribute is not compatible with its type, it's converted to the declared type invoking the constructor `new Type( value )` (just `Type( value )` for primitives).
133+
134+
```javascript
135+
@define class Book extends Record {
136+
static attributes = {
137+
title : '',
138+
author : ''
139+
}
140+
}
141+
142+
const book = new Book({
143+
title: "One Thousand and One Nights",
144+
author: "Scheherazade"
145+
});
146+
```
147+
148+
### `abstract` record.initialize( attrs?, options? )
149+
150+
Called at the end of the `Record` constructor when all attributes are assigned and the record's inner state is properly initialized. Takes the same arguments as
151+
a constructor.
152+
153+
### record.attrName
154+
155+
Record's attributes may be directly accessed as `record.name`.
156+
157+
> Please note, that you *have to declare all attributes* in `static attributes` declaration.
158+
159+
```javascript
160+
@define class Account extends Record {
161+
static attributes = {
162+
name : String,
163+
balance : Number
164+
}
165+
}
166+
167+
const myAccount = new Account({ name : 'mine' });
168+
myAccount.balance += 1000000; // That works. Good, eh?
169+
```
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Update record attributes
2+
3+
### record.attrName = value
4+
5+
Assign the record's attribute. If the value is not compatible with attribute's type from the declaration, it is converted:
6+
7+
- with `Type( value )` call, for primitive types;
8+
- with `record.attrName.set( value )`, for existing record or collection (updated in place);
9+
- with `new Type( value )` in all other cases.
10+
11+
Record triggers events on changes:
12+
- `change:attrName` *( record, value )*.
13+
- `change` *( record )*.
14+
15+
```javascript
16+
@define class Book extends Record {
17+
static attributes = {
18+
title : String,
19+
author : String
20+
price : Number,
21+
publishedAt : Date,
22+
available : Boolean
23+
}
24+
}
25+
26+
const myBook = new Book({ title : "State management with Type-R" });
27+
myBook.author = 'Vlad'; // That works.
28+
myBook.price = 'Too much'; // Converted with Number( 'Too much' ), resulting in NaN.
29+
myBook.price = '123'; // = Number( '123' ).
30+
myBook.publishedAt = new Date(); // Type is compatible, no conversion.
31+
myBook.publishedAt = '1678-10-15 12:00'; // new Date( '1678-10-15 12:00' )
32+
myBook.available = some && weird || condition; // Will always be Boolean. Or null.
33+
```
34+
35+
### record.set( { attrName : value, ... }, options? : `options` )
36+
37+
Bulk assign record's attributes, possibly taking options.
38+
39+
If the value is not compatible with attribute's type from the declaration, it is converted:
40+
41+
- with `Type( value )` call, for primitive types.
42+
- with `record.attrName.set( value )`, for existing record or collection (updated in place).
43+
- with `new Type( value )` in all other cases.
44+
45+
Record triggers events after all changes are applied:
46+
47+
1. `change:attrName` *( record, val, options )* for any changed attribute.
48+
2. `change` *(record, options)*, if there were changed attributes.
49+
50+
### `options` { parse : true }
51+
52+
Assume `record.set` argument is the raw JSON and parse it. Must be used to process the response from the server.
53+
54+
```javascript
55+
// Another way of doing the bestSeller.clone()
56+
// Amazingly, this is guaranteed to work by default.
57+
const book = new Book();
58+
book.set( bestSeller.toJSON(), { parse : true } );
59+
```
60+
61+
### record.assignFrom( otherRecord )
62+
63+
Makes an existing `record` to be the full clone of `otherRecord`, recursively assigning all attributes.
64+
65+
```javascript
66+
// Another way of doing the bestSeller.clone()
67+
const book = new Book();
68+
book.assignFrom( bestSeller );
69+
```
70+
71+
# Transactions
72+
73+
All record updates occurs in the scope of transactions. Transaction is the sequence of changes which results in a single `change` event.
74+
75+
Transaction can be opened either manually or implicitly with calling `set()` or assigning an attribute.
76+
Any additional changes made to the record in `change:attr` event handler will be executed in the scope of the original transaction, and won't trigger additional `change` events.
77+
78+
### record.transaction( fun )
79+
80+
Execute the all changes made to the record in `fun` as single transaction triggering the single `change` event.
81+
82+
```javascript
83+
some.record.transaction( record => {
84+
record.a = 1; // `change:a` event is triggered.
85+
record.b = 2; // `change:b` event is triggered.
86+
}); // `change` event is triggered.
87+
```
88+
89+
Manual transactions with attribute assignments are superior to `record.set()` in terms of both performance and flexibility.

0 commit comments

Comments
 (0)