Getting started
Type-R is the modern JS state framework supporting observable changes, state validation, JSON serialization and promised I/O. It's designed to meet the requirements of the complex JS data layer on both front-end and back-end.
Features:
- Deeply observable changes
- The series of updates can be grouped to transactions emitting the single change event.
- There are fine-grained change events allowing the cascade of reactions executed as a single transaction.
- Serialization and persistence
- Type-R data structures are serializable to JSON by default.
- Support for both nested JSON and relationship by id.
- JSON mapping can be customized for every particular attribute or class.
- I/O endpoints abstract out the persistence protocol.
- Lazily evaluated validation
- The validation is performed on the first access to the validation error field.
- Validation results are cached and never computed twice for unchanged data.
- Dynamic type safety
- Types are asserted every
- The shape of both data structures and generated JSON is guaranteed to match the definitions.
- Performance
- Type-R data structures are designed to handle large collections (10K elements and more) with sub-second delays.
- Updates are about 10 times more efficient than BackboneJS models and collections.
Type-R is completely unopinionated on the client-server transport protocol and the view layer technology. It's your perfect M and VM in modern MVVM or MVC architecture.
import { define, Record } from 'type-r'
// Define email attribute type with encapsulated validation check.
const Email = String.has.check( x => x! || x.indexOf( '@' ) >= 0, 'Invalid email' );
@define class User extends Record {
static attributes = {
name : String.isRequired, // should not be empty for the record to be valid.
email : Email.isRequired
}
}
@define class Message extends Record {
static attributes = {
created : Date // = new Date()
author : User, // aggregated User record.
to : User.Collection, // aggregating collection of users
subject : '',
body : ''
}
}
const msg = new Message();
assert( !msg.isValid() ); // Is not valid because msg.author has empty attributes
// Listen for the changes in aggregation tree...
msg.on( 'change', () => console.log( 'change!!!' ) );
msg.transaction( () => { // Prepare to make the sequence of changes on msg
msg.author.name = 'John Dee'; // No 'change' event yet as we're in the transaction.
msg.author.email =<EMAIL_ADDRESS>assert( msg.isValid() ); // Now msg is valid as all of its attributes are valid.
}); // Got single 'change!!!' message in the console.
Installation and requirements
Is packed as UMD and ES6 module. No peer dependencies are required.
npm install type-r --save-dev
How the Type-R compares with X?
Type-R started to develop in 2014 as the modern substitution for BackboneJS, which would retain the spirit of the BackboneJS simplicity but would be an order of magnitude faster and superior to Ember Data in its capabilities to describe complex data.
The closest things to the Type-R are Ember Data, BackboneJS models and collections, and mobx.
There are both similarities and differences:
- In contrast to mobx, Type-R detects deeply nested changes.
- Records and Collections resembles Backbone's Models/Collections, but Record is not an object hash but class and it's updates are ~10 times faster.
- Data validation is comparable to one in Ember Data, but it's lazily evaluated and cached.
- id-relationship resembles one in Ember Data, which was used a source of inspiration. But Type-R supports the first-class aggregation as well.
- Like Ember Data, Type-R supports abstract data adapters (I/O endpoints).
- Unlike Ember and Backbone, Type-R is unopinionated on the view layer and routing. Like in mobx, there are React bindings.
Speaking of the distinguishing differences,
- Type-R encourages using of the layered application state instead of the global singleton store. In Type-R, the stores are the special kind of records which can participate in dynamically configured lookup chains. There might be as many dynamically created and disposed stores as you need, starting with no stores at all.
- Type-R's records are protected from improper assignment with run-time type assertions and conversions.
- Type-R distinguishes aggregation and the plain association operating with aggregation trees formed by nested records and collections. Aggregation tree is serialized as nested JSON. Operations like
clone()
,dispose()
,isValid()
andtoJSON()
are performed recursively on elements of aggregation tree gracefully handling the references to shared objects.
| Feature | Type-R | Backbone Models | Ember Data | mobx |
|---|---|---|---|---|
| Observable changes in object graph | ✓ | - | - | ✓ |
| JSON Serialization | ✓ | ✓ | ✓ | - |
| Validation | ✓ | ✓ | ✓ | - |
| Dynamic Type Safety | ✓ | - | for serialization only | - |
| Aggregation | ✓ | - | - | - |
| Relations by id | ✓ | - | ✓ | - |
| Generalized I/O | ✓ | sync function | ✓ | - |
Record
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
.
import { define, Record } from 'type-r'
// ⤹ required to make magic work
@define class User extends Record {
// ⤹ attribute's declaration
static attributes = {
firstName : '', // ⟵ String type is inferred from the default value
lastName : String, // ⟵ Or you can just mention its constructor
email : String.value( null ), //⟵ Or you can provide both
createdAt : Date, // ⟵ And it works for any constructor.
// And you can attach ⤹ metadata to fine-tune attribute's behavior
lastLogin : Date.value( null ).has.toJSON( false ) // ⟵ not serializable
}
}
const user = new User();
console.log( user.createdAt ); // ⟵ this is an instance of Date created for you.
const users = new User.Collection(); // ⟵ Collections are defined automatically.
users.on( 'changes', () => updateUI( users ) ); // ⟵ listen to the changes.
users.set( json, { parse : true } ); // ⟵ parse raw JSON from the server.
users.updateEach( user => user.firstName = '' ); // ⟵ bulk update triggering 'changes' once
Definition
Record is defined as a regular ES6 class, except:
- it must extend the
Record
base class; - it must be preceded with the
@define
decorator; - it should use
static attributes
to declare attributes.
Attributes declaration is an object with attribute names mapped to their type annotation and/or default value. Once defined, attributes can be accessed as regular class members.
The minimal record definition looks like this:
@define class MyRecord extends Record {
static attributes = {
name : ''
}
}
decorator
@define
Must be placed before the class definition.
import { define, Record } from 'type-r'
@define class X extends Record {
...
}
static
attributes = { name : attrDef
, ... }
Record's attributes definition. Lists attribute names along with their types, default values, and metadata controlling different aspects of attribute behavior.
@define class User extends Record {
static attributes = {
name : String.value( 'John Dow' ),
email<EMAIL_ADDRESS>// Same as String.value<EMAIL_ADDRESS>)
address : String, // Same as String.value( '' )
}
}
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).
static
idAttribute = 'attrName'
A record's unique identifier is stored under the pre-defined id
attribute.
If you're directly communicating with a backend (CouchDB, MongoDB) that uses a different unique key, you may set a Record's idAttribute
to transparently map from that key to id.
Record's id
property will still be linked to Record's id, no matter which value idAttribute
has.
@define class Meal extends Record {
static idAttribute = "_id";
static attributes = {
_id : Number,
name : ''
}
}
const cake = new Meal({ _id: 1, name: "Cake" });
alert("Cake id: " + cake.id);
attrDef
: Type
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.
@define class Person extends Record {
static attributes = {
name : String // String attribute which is "" by default.
createdAt : Date // Date attribute
...
}
}
attrDef
: defaultValue
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.
Use the general form of attribute definition for attributes of Function
type: Function.value( theFunction )
.
@define class GridColumn extends Record {
static attributes = {
name : '', // String attribute which is '' by default.
render : Function.value( x => x ),
...
}
}
attrDef
: Type.value( defaultValue )
The general form of attribute definition is Type.value( defaultValue )
, where the Type
is the corresponding constructor function.
@define class Person extends Record {
static attributes = {
phone : String.value( null ) // String attribute which is null by default.
...
}
}
The record is recursive if it's uses the type of itself in its attribute definition.
attrDef
: Date
Date attribute initialized as new Date()
. Represented in JSON as string or number depending on the type:
Date
- as ISO date string.Date.microsoft
- as Microsoft's"/Date(msecs)/"
string.Date.timestamp
- as UNIX integer timestamp.
static
Collection
The default record's collection class automatically defined for every Record subclass. Can be referenced as Record.Collection
.
May be explicitly assigned in record's definition with custom collection class.
// Declare the collection class.
@define class Comments extends Record.Collection {}
@define class Comment extends Record{
static Collection = Comments; // Make it the default Comment collection.
static attributes = {
text : String,
replies : Comments
}
}
attrDef
Type.has and type( Type )
Attribute definition can have different metadata attached which affects various aspects of attribute's behavior. Metadata is attached with
a chain of calls after the Ctor.has
property or type( Ctor )
call. Attribute's default value is the most common example of such a metadata and is the single option which can be applied to the constructor function directly.
import { define, type, Record }
@define class Dummy extends Record {
static attributes = {
a : String.has.value( "a" ), // Same as String.value( "a" )
b : type( String ).value( "a" ) // Same as above
}
}
Definitions in TypeScript
Type-R implements experimental support for TypeScript record definitions.
decorator
@attr
The simplest form of attribute definition. Requires reflect-metadata
npm package and emitDecoratorMetadata
option set to true in the tsconfig.json
.
No metadata can be attached.
import { define, attr, Record } from 'type-r'
@define class User extends Record {
@attr name : string
@attr email : string
}
decorator
@attr( attrDef )
Long form of the attribute definition. Accepts arbitraty Type-R attribute definition as a parameter. Could be used if metadata must be attached to an attributed.
Doesn't require reflect-metadata
package.
import { define, attr, Record } from 'type-r'
@define class User extends Record {
@attr( String.value( 5 ) ) name : string
@attr( "5" ) name : string // String type is infered from the default value.
@attr( String.has.toJSON( false ) ) email : string
}
decorator
@type( Ctor ).as
Attribute definition has .as
property which converts the definition to the attribute's decorator. Can be used in conjunction with type()
function to provide an
alternative syntax for attribute definitions.
import { define, type, Record } from 'type-r'
@define class User extends Record {
@type( String ).value( 5 ).as name : string
@type( String ).toJSON( false ).as email : string
}
Create and dispose
Record behaves as regular ES6 class with attributes accessible as properties.
new Record()
Create an instance of the record with default attribute values taken from the attributes definition.
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.
new Record({ attrName : value, ... }, options? )
When creating an instance of a record, you can pass in the initial attribute values to override the defaults.
If {parse: true}
option is used, attrs
is assumed to be the JSON.
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).
@define class Book extends Record {
static attributes = {
title : '',
author : ''
}
}
const book = new Book({
title: "One Thousand and One Nights",
author: "Scheherazade"
});
record.clone()
Create the deep copy of the aggregation tree, recursively cloning all aggregated records and collections. References to shared members will be copied, but not shared members themselves.
callback
record.initialize( attrs?, options? )
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
a constructor.
record.dispose()
Recursively dispose the record and its aggregated members. "Dispose" means that elements of the aggregation tree will unsubscribe from all event sources. It's crucial to prevent memory leaks in SPA.
The whole aggregation tree will be recursively disposed, shared members won't.
Read and Update
record.cid
Read-only client-side record's identifier. Generated upon creation of the record and is unique for every record's instance. Cloned records will have different cid
.
record.id
Predefined record's attribute, the id
is an arbitrary string (integer id or UUID). id
is typically generated by the server. It is used in JSON for id-references.
Records can be retrieved by id
from collections, and there can be just one instance of the record with the same id
in the particular collection.
record.isNew()
Has this record been saved to the server yet? If the record does not yet have an id
, it is considered to be new.
record.attrName
Record's attributes may be directly accessed as record.name
.
@define class Account extends Record {
static attributes = {
name : String,
balance : Number
}
}
const myAccount = new Account({ name : 'mine' });
myAccount.balance += 1000000; // That works. Good, eh?
record.attrName = value
Assign the record's attribute. If the value is not compatible with attribute's type from the declaration, it is converted:
- with
Type( value )
call, for primitive types; - with
record.attrName.set( value )
, for existing record or collection (updated in place); - with
new Type( value )
in all other cases.
Record triggers events on changes:
change:attrName
( record, value ).change
( record ).
@define class Book extends Record {
static attributes = {
title : String,
author : String
price : Number,
publishedAt : Date,
available : Boolean
}
}
const myBook = new Book({ title : "State management with Type-R" });
myBook.author = 'Vlad'; // That works.
myBook.price = 'Too much'; // Converted with Number( 'Too much' ), resulting in NaN.
myBook.price = '123'; // = Number( '123' ).
myBook.publishedAt = new Date(); // Type is compatible, no conversion.
myBook.publishedAt = '1678-10-15 12:00'; // new Date( '1678-10-15 12:00' )
myBook.available = some && weird || condition; // Will always be Boolean. Or null.
record.set( { attrName : value, ... }, options? : options
)
Bulk assign record's attributes, possibly taking options.
If the value is not compatible with attribute's type from the declaration, it is converted:
- with
Type( value )
call, for primitive types. - with
record.attrName.set( value )
, for existing record or collection (updated in place). - with
new Type( value )
in all other cases.
Record triggers events after all changes are applied:
change:attrName
( record, val, options ) for any changed attribute.change
(record, options), if there were changed attributes.
record.assignFrom( otherRecord )
Makes an existing record
to be the full clone of otherRecord
, recursively assigning all attributes.
In contracts to record.clone()
, the record is updated in place.
// Another way of doing the bestSeller.clone()
const book = new Book();
book.assignFrom( bestSeller );
record.transaction( fun )
Execute the all changes made to the record in fun
as single transaction triggering the single change
event.
All record updates occurs in the scope of transactions. Transaction is the sequence of changes which results in a single change
event.
Transaction can be opened either manually or implicitly with calling set()
or assigning an attribute.
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.
some.record.transaction( record => {
record.a = 1; // `change:a` event is triggered.
record.b = 2; // `change:b` event is triggered.
}); // `change` event is triggered.
Manual transactions with attribute assignments are superior to record.set()
in terms of both performance and flexibility.
attrDef
: Type.has.get( hook
)
Attach get hook to the record's attribute. hook
is the function of signature ( value, attr ) => value
which is used to transform the attribute's value before it will be read. Hook is executed in the context of the record.
attrDef
: Type.has.set( hook
)
Attach the set hook to the record's attribute. hook
is the function of signature ( value, attr ) => value
which is used to transform the attribute's value before it will be assigned. Hook is executed in the context of the record.
If set hook will return undefined
, it will cancel attribute update.
Nested records and collections
Record's attributes can hold other Records and Collections, forming indefinitely nested data structures of arbitrary complexity. To create nested record or collection you should just mention its constructor function in attribute's definition.
import { Record } from 'type-r'
@define class User extends Record {
static attributes = {
name : String,
email : String,
isActive : true
}
}
@define class UsersListState extends Record {
static attributes = {
users : User.Collection
}
}
All nested records and collections are aggregated by default and behave as integral parts of the containing record. Aggregated attributes are exclusively owned by the record, and taken with it together form an ownership tree. Many operations are performed recursively on aggregated elements:
- They are created when the owner record is created.
- They are cloned when the record is cloned.
- They are disposed when the record is disposed.
- They are validated as part of the record.
- They are serialized as nested JSON.
The nature of aggregation relationship in OO is explained in this article.
attrDef
: RecordOrCollection
Aggregated record or collection. Represented as nested object or array in record's JSON. Aggregated members are owned by the record and treated as its integral part (recursively created, cloned, serialized, validated, and disposed). One object can have single owner. The record with its aggregated attributes forms an aggregation tree.
All changes in aggregated record or collections are detected and cause change events on the containing record.
record.getOwner()
Return the record which is an owner of the current record, or null
there are no one.
Due to the nature of aggregation, an object may have one and only one owner.
record.collection
Return the collection which aggregates the record, or null
if there are no one.
attrDef
: RecordOrCollection.shared
Non-serializable reference to the record or collection possibly from the different aggregation tree. Initialized with null
. Is not recursively cloned, serialized, validated, or disposed.
All changes in shared records or collections are detected and cause change events of the containing record.
@define class UsersListState extends Record {
static attributes = {
users : User.Collection,
selected : User.shared // Can be assigned with the user from this.users
}
}
attrDef
: Collection.Refs
Non-aggregating collection. Collection of references to shared records which itself is aggregated by the record, but does not aggregate its elements. In contrast to the Collection.shared
, Collection.Refs
creates an instance of collection which is the part the parent record.
The collection itself is recursively created and cloned. However, its records are not aggregated by the collection thus they are not recursively cloned, validated, serialized, or disposed.
All changes in the collection and its elements are detected and cause change events of the containing record.
@define class MyRecord extends Record {
static attributes = {
notCloned : SomeCollection.shared, // Reference to the _shared collection_ object.
cloned : SomeCollection.Refs // _Aggregated_ collection of references to the _shared records_.
}
decorator
@predefine
Make forward declaration for the record to define its attributes later with RecordClass.define()
. Used instead of @define
for recursive record definitions.
Creates the default RecordClass.Collection
type which can be referenced in attribute definitions.
static
define({ attributes : { name : attrDef
, ... } })
May be called to define attributes in conjunction with @predefine
decorator to make recursive record definitions.
@predefine class Comment extends Record{}
Comment.define({
attributes : {
text : String,
replies : Comment.Collection
}
});
Collection
Collections are ordered sets of records. You can bind "changes" events to be notified when the collection has been modified, listen for the record "add", "remove", and "change" events, and use a full suite of iteration methods.
// Implicitly defined collection.
const books = new Book.Collection();
@define
class ComicsShelve extends Book.Collection {
static itemEvents = {
// List of records's events we want to be triggered on the collection
'change:inMyReadingList' : true,
'customEvent' : true
}
}
@define
class Comics extends Book {
// Use custom collection instead of the implicitly created one
static Collection = ComicsShelve;
// Extend record's attributes
static attributes = {
artist : Author
}
}
Definition
RecordClass.Collection
Default collection constructor for the given Record class.
MyRecord.Collection
is defined automatically for every record class. In most cases, there is no need to define collections explicitly.
CollectionClass.Refs
Non-aggregating collection constructor.
By default, the collection aggregates its elements, which are treated as an integral part of the collection (serialized, cloned, disposed, and validated recursively). An aggregation means the single owner, as the single object cannot be an integral part of two distinct things.
Collection.Refs
doesn't aggregate its elements, and it's not mapped to JSON. It is useful for the local application state only.
static
model = RecordConstructor
Part of the collection's definition specifying the record type. The collection must know the type of its records to restore its elements from JSON properly.
When not specified, the collection can hold any Record subclass, but it cannot deserialize itself.
@define class Library extends Record.Collection {
static model = Book;
}
static
comparator = 'attrName'
Maintain the collection in sorted order by the given record's attribute.
static
comparator = x => number | string
Maintain the collection in sorted order according to the "sortBy" comparator function.
"sortBy" comparator functions take a record and return a numeric or string value by which the record should be ordered relative to others.
static
comparator = ( x, y ) => -1 | 0 | 1
Maintain the collection in sorted order according to the "sort" comparator function.
"sort" comparator functions take two records, and return -1 if the first record should come before the second, 0 if they are of the same rank and 1 if the first record should come after.
Note how even though all of the chapters in this example are added backwards, they come out in the proper order:
@define class Chapter extends Record {
static attributes = {
page : Number,
title : String
}
}
var chapters = new Chapter.Collection();
chapters.comparator = 'page';
chapters.add(new Chapter({page: 9, title: "The End"}));
chapters.add(new Chapter({page: 5, title: "The Middle"}));
chapters.add(new Chapter({page: 1, title: "The Beginning"}));
alert(chapters.map( x => x.title ));
Create and dispose
new Collection( records?, options? )
Create an aggregating serializable collection of records. The collection will take an ownership on its records and will put an error in the console if it can't.
When creating a Collection, you may choose to pass in the initial array of records. The collection's comparator may be included as an option. Passing false
as the comparator option will prevent sorting. If you define an initialize()
function, it will be invoked when the collection is created.
var tabs = new TabSet([tab1, tab2, tab3]);
new Collection.Refs( records?, options? )
Create a non-aggregating non-serializable collection. The collection does not take ownership in its records. In all other aspects it behaves as the regular collection.
callback
collection.initialize( records?, options? )
Initialization function which is called at the end of the constructor.
collection.clone()
Clone the collection. Aggregating collection will be recursively cloned, non-aggregated collections will be shallow cloned.
collection.dispose()
Dispose the collection. Aggregating collection will recursively dispose its records.
Read and iterate
collection.get( id )
Get a record from a collection, specified by an id
, a cid
, or by passing in a record.
const book = library.get(110);
collection.at( index )
Get a record from a collection, specified by index. Useful if your collection is sorted, and if your collection isn't sorted, at will still retrieve records in insertion order. When passed a negative index, it will retrieve the record from the back of the collection.
collection.length
Like an array, a Collection maintains a length property, counting the number of records it contains.
collection.models
Raw access to the JavaScript array of records inside of the collection. Usually you'll want to use get
, at
, or the other methods to access record objects, but occasionally a direct reference to the array is desired.
collection.slice( begin, end )
Return a shallow copy of the collection.models
, using the same options as native Array#slice.
collection.indexOf( recordOrId : any ) : number
Return an index of the record in the collection, and -1 if there are no such a record in the collection.
Can take the record itself as an argument, id
, or cid
of the record.
collection.forEach( iteratee : ( val : Record, index ) => void, context? )
Same as collection.each()
.
collection.each( iteratee : ( val : Record, index ) => void, context? )
Iterate through the elements of the collection. Similar to Array.forEach
.
collection.map( iteratee : ( val : Record, index ) => T, context? )
Map elements of the collection. Similar to Array.map
, but undefined
values returned by iteratee are filtered out.
Thus, collection.map
can be used to map and filter elements in a single pass.
collection.filter( iteratee : Predicate, context? )
Return filtered array of records matching the predicate.
Predicate is either the iteratee function returning boolean, or an object with attribute values used to match with record's attributes.
collection.every( iteratee : Predicate, context? ) : boolean
Return true
if all records match the predicate.
collection.some( iteratee : Predicate, context? ) : boolean
Return true
if at least one record match the predicated.
By default there is no comparator for a collection. If you define a comparator, it will be used to maintain the collection in sorted order. This means that as records are added, they are inserted at the correct index in collection.models
.
Note that Type-R depends on the arity of your comparator function to determine between the two styles, so be careful if your comparator function is bound.
Collections with a comparator will not automatically re-sort if you later change record attributes, so you may wish to call sort after changing record attributes that would affect the order.
Update
Methods to update the collection. They accept common options:
sort : false
- do not sort the collection.parse : true
- parse raw JSON (used to set collection with a data from the server).
collection.add( records, options? )
Add a record (or an array of records) to the collection. If this is the Record.Collection
, you may also pass raw attributes objects, and have them be vivified as instances of the Record
. Returns the added (or preexisting, if duplicate) records.
Pass {at: index}
to splice the record into the collection at the specified index. If you're adding records to the collection that are already in the collection, they'll be ignored, unless you pass {merge: true}
, in which case their attributes will be merged into the corresponding records.
- Trigger the one event per record:
add
(record, collection, options) for each record added.change
(record, options) for each record changed (if the{merge: true}
option is passed).
- Trigger the single event:
update
(collection, options) if any records were added.sort
(collection, options) if an order of records was changed.
- Trigger
changes
event in case if any changes were made to the collection and objects inside.
collection.remove( records, options? )
Remove a record (or an array of records) from the collection, and return them. Each record can be a record instance, an id string or a JS object, any value acceptable as the id argument of collection.get.
- Trigger
remove
(record, collection, options) for each record removed. - If any records were removed, trigger:
update
(collection, options)changes
(collection, options).
collection.set( records, options? )
The set method performs a "smart" update of the collection with the passed list of records. If a record in the list isn't yet in the collection it will be added; if the record is already in the collection its attributes will be merged; and if the collection contains any records that aren't present in the list, they'll be removed. All of the appropriate "add", "remove", and "change" events are fired as this happens. Returns the touched records in the collection. If you'd like to customize the behavior, you can disable it with options: {remove: false}
, or {merge: false}
.
Events
- Trigger the one event per record:
add
(record, collection, options) for each record added.remove
(record, collection, options) for each record removed.change
(record, options) for each record changed.
- Trigger the single event:
update
(collection, options) if any records were added.sort
(collection, options) if an order of records was changed.
- Trigger
changes
event in case if any changes were made to the collection and objects inside.
const vanHalen = new Man.Collection([ eddie, alex, stone, roth ]);
vanHalen.set([ eddie, alex, stone, hagar ]);
// Fires a "remove" event for roth, and an "add" event for hagar.
// Updates any of stone, alex, and eddie's attributes that may have
// changed over the years.
collection.assignFrom( otherCollection )
Synchronize the state of the collection and its aggregation tree with other collection of the same type. Updates existing objects in place. Record in the collection is considered to be "existing" if it has the same id
.
Equivalent to collection.set( otherCollection.models, { merge : true } )
and triggers similar events on change.
collection.reset( records, options? )
Replace the collection's content with the new records. More efficient than collection.set
, but does not send record-level events.
Calling collection.reset()
without passing any records as arguments will empty the entire collection.
- Trigger event
reset
( collection, options ). - Trigger event
changes
( collection, options ).
collection.sort( options? )
Force a collection to re-sort itself. You don't need to call this under normal circumstances, as a collection with a comparator will sort itself whenever a record is added. To disable sorting when adding a record, pass {sort: false}
to add. Calling sort triggers a "sort" event on the collection.
collection.push( record, options? )
Add a record at the end of a collection. Takes the same options as add.
collection.pop( options? )
Remove and return the last record from a collection. Takes the same options as remove.
collection.unshift( record, options? )
Add a record at the beginning of a collection. Takes the same options as add.
collection.shift( options? )
Remove and return the first record from a collection. Takes the same options as remove.
Observable Changes
Overview
Type-R implements deeply observable changes on the object graph constructed of records and collection.
All of the record and collection updates happens in a scope of the transaction followed by the change event. Every record or collection update operation opens implicit transaction. Several update operations can be groped to the single explicit transaction if executed in the scope of the obj.transaction()
or col.updateEach()
call.
@define class Author extends Record {
static attributes = {
name : ''
}
}
@define class Book extends Record {
static attributes = {
name : '',
datePublished : Date,
author : Author
}
}
const book = new Book();
book.on( 'change', () => console.log( 'Book is changed') );
// Implicit transaction, prints to the console
book.author.name = 'John Smith';
Record
Events mixin methods (7)
Record implements Events mixin.
event
"change" ( record )
Triggered by the record at the end of the attributes update transaction in case if there were any changes applied.
event
"change:attrName" ( record, value )
Triggered by the record during the attributes update transaction for every changed attribute.
attrDef
: Type.has.watcher( watcher )
Attach change:attr
event listener to the particular record's attribute. watcher
can either be the record's method name or the function ( newValue, attr ) => void
. Watcher is always executed in the context of the record.
@define class User extends Record {
static attributes = {
name : String.has.watcher( 'onNameChange' ),
isAdmin : Boolean,
}
onNameChange(){
// Cruel. But we need it for the purpose of the example.
this.isAdmin = this.name.indexOf( 'Admin' ) >= 0;
}
}
attrDef
: Type.has.changeEvents( false )
Turn off changes observation for nested records or collections.
Record automatically listens to change events of all nested records and collections, triggering appropriate change events for its attributes. This declaration turns it off for the specific attribute.
attrDef
: Type.has.events({ eventName : handler, ... })
Automatically manage custom event subscription for the attribute. handler
is either the method name or the handler function.
record.changed
The changed
property is the internal hash containing all the attributes that have changed during its last transaction.
Please do not update changed
directly since its state is internally maintained by set()
.
A copy of changed
can be acquired from changedAttributes()
.
record.changedAttributes( attrs? )
Retrieve a hash of only the record's attributes that have changed during the last transaction, or false if there are none. Optionally, an external attributes hash can be passed in, returning the attributes in that hash which differ from the record. This can be used to figure out which portions of a view should be updated, or what calls need to be made to sync the changes to the server.
record.previous( attr )
During a "change" event, this method can be used to get the previous value of a changed attribute.
@define class Person extends Record{
static attributes = {
name: ''
}
}
const bill = new Person({
name: "Bill Smith"
});
bill.on("change:name", ( record, name ) => {
alert( `Changed name from ${ bill.previous('name') } to ${ name }`);
});
bill.name = "Bill Jones";
record.previousAttributes()
Return a copy of the record's previous attributes. Useful for getting a diff between versions of a record, or getting back to a valid state after an error occurs.
Collection
All changes in the records cause change events in the collections they are contained in.
Subset collections is an exception; they don't observe changes of its elements by default.
Events mixin methods (7)
Collection implements Events mixin.
collection.transaction( fun )
Execute the sequence of updates in fun
function in the scope of the transaction.
All collection updates occurs in the scope of transactions. Transaction is the sequence of changes which results in a single changes
event.
Transaction can be opened either manually or implicitly with calling any of collection update methods.
Any additional changes made to the collection or its items in event handlers will be executed in the scope of the original transaction, and won't trigger an additional changes
events.
collection.updateEach( iteratee : ( val : Record, index ) => void, context? )
Similar to the collection.each
, but wraps an iteration in a transaction. The single changes
event will be emitted for the group of changes to the records made in updateEach
.
static
itemEvents = { eventName : handler
, ... }
Subscribe for events from records. The hander
is either the collection's method name, the handler function, or true
.
When true
is passed as a handler, the corresponding event will be triggered on the collection.
event
"changes" (collection, options)
When collection has changed. Single event triggered when the collection has been changed.
event
"reset" (collection, options)
When the collection's entire contents have been reset (reset()
method was called).
event
"update" (collection, options)
Single event triggered after any number of records have been added or removed from a collection.
event
"sort" (collection, options)
When the collection has been re-sorted.
event
"add" (record, collection, options)
When a record is added to a collection.
event
"remove" (record, collection, options)
When a record is removed from a collection.
event
"change" (record, options)
When a record inside of the collection is changed.
Events mixin
Type-R uses an efficient synchronous events implementation which is backward compatible with Backbone 1.1 Events API but is about twice faster in all major browsers. It comes in form of Events
mixin and the Messenger
base class.
Events
is a mixin giving the object the ability to bind and trigger custom named events. Events do not have to be declared before they are bound, and may take passed arguments.
Both source
and listener
mentioned in method signatures must implement Events methods.
import { mixins, Events } from 'type-r'
@mixins( Events )
class EventfulClass {
...
}
source.trigger(event, arg1, arg2, ... )
Trigger callbacks for the given event, or space-delimited list of events. Subsequent arguments to trigger will be passed along to the event callbacks.
listener.listenTo(source, event, callback)
Tell an object to listen to a particular event on an other object. The advantage of using this form, instead of other.on(event, callback, object), is that listenTo allows the object to keep track of the events, and they can be removed all at once later on. The callback will always be called with object as context.
view.listenTo(record, 'change', view.render );
listener.stopListening([source], [event], [callback])
Tell an object to stop listening to events. Either call stopListening with no arguments to have the object remove all of its registered callbacks ... or be more precise by telling it to remove just the events it's listening to on a specific object, or a specific event, or just a specific callback.
view.stopListening(); // Unsubscribe from all events
view.stopListening(record); // Unsubscribe from all events from the record
listener.listenToOnce(source, event, callback)
Just like listenTo()
, but causes the bound callback to fire only once before being automatically removed.
source.on(event, callback, [context])
Bind a callback function to an object. The callback will be invoked whenever the event is fired. If you have a large number of different events on a page, the convention is to use colons to namespace them: poll:start
, or change:selection
. The event string may also be a space-delimited list of several events...
book.on("change:title change:author", ...);
Callbacks bound to the special "all" event will be triggered when any event occurs, and are passed the name of the event as the first argument. For example, to proxy all events from one object to another:
proxy.on("all", function(eventName) {
object.trigger(eventName);
});
All event methods also support an event map syntax, as an alternative to positional arguments:
book.on({
"change:author": authorPane.update,
"change:title change:subtitle": titleView.update,
"destroy": bookView.remove
});
To supply a context value for this when the callback is invoked, pass the optional last argument: record.on('change', this.render, this)
or record.on({change: this.render}, this)
.
source.off([event], [callback], [context])
Remove a previously bound callback function from an object. If no context is specified, all of the versions of the callback with different contexts will be removed. If no callback is specified, all callbacks for the event will be removed. If no event is specified, callbacks for all events will be removed.
// Removes just the `onChange` callback.
object.off("change", onChange);
// Removes all "change" callbacks.
object.off("change");
// Removes the `onChange` callback for all events.
object.off(null, onChange);
// Removes all callbacks for `context` for all events.
object.off(null, null, context);
// Removes all callbacks on `object`.
object.off();
Note that calling record.off()
, for example, will indeed remove all events on the record — including events that Backbone uses for internal bookkeeping.
source.once(event, callback, [context])
Just like on()
, but causes the bound callback to fire only once before being removed. Handy for saying "the next time that X happens, do this". When multiple events are passed in using the space separated syntax, the event will fire once for every event you passed in, not once for a combination of all events
Built-in events
All Type-R objects implement Events mixin and use events to notify listeners on changes.
Record and Store change events:
| Event name | Handler arguments | When triggered |
|---|---|---|
| change | (record, options) | At the end of any changes. |
| change:attrName | (record, value, options) | The record's attribute has been changed. |
Collection change events:
| Event name | Handler arguments | When triggered |
|---|---|---|
| changes | (collection, options) | At the end of any changes. |
| reset | (collection, options) | reset() method was called. |
| update | (collection, options) | Any records added or removed. |
| sort | (collection, options) | Order of records is changed. |
| add | (record, collection, options) | The record is added to a collection. |
| remove | (record, collection, options) | The record is removed from a collection. |
| change | (record, options) | The record is changed inside of collection. |
Messenger class
Messenger is an abstract base class implementing Events mixin and some convenience methods.
import { define, Messenger } from 'type-r'
class MyMessenger extends Messenger {
}
Events mixin methods (7)
Messenger implements Events mixin.
messenger.cid
Unique run-time only messenger instance id (string).
callback
messenger.initialize()
Callback which is called at the end of the constructor.
messenger.dispose()
Executes messenger.stopListening()
and messenger.off()
.
Objects must be disposed to prevent memory leaks caused by subscribing for events from singletons.
Validation
Overview
Type-R validation mechanics based on following principles:
- Validation is performed recursively on the aggregation tree formed by nested records and collections. If an element at the bottom of the tree is not valid, the whole object tree is not valid.
- Validation rules can be defined for record's attribute, record, and collection.
- Validation happens automatically on the first read of the validation error. There's no special API to trigger the validation.
- Validation results are cached across the aggregation tree, thus consequent validation error reads are fast. Changed parts of aggregation tree will be validated again when necessary.
Record's attributes
attrDef
: Type.has.check( predicate, errorMsg? )
Attribute-level validator.
predicate : value => boolean
is the function taking attribute's value and returningtrue
whenever the value is valid.- optional
errorMsg
is the error message which will be passed in case if the validation fail.
If errorMsg
is omitted, error message will be taken from predicate.error
. It makes possible to define reusable validation functions.
function isAge( years ){
return years >= 0 && years < 200;
}
isAge.error = "Age must be between 0 and 200";
Attribute may have any number of checks attached, which are execute in sequence. Validation stops when first check in sequence fails.
// Define new attribute metatype encapsulating validation checks.
const Age = Number.has
.check( x => x >= 0, 'I guess you are a bit older' )
.check( x => x < 200, 'No way man can be that old' );
attrDef
: Type.isRequired
The special case of attribute-level check cutting out empty values. Attribute value must be truthy to pass, "Required"
is used as validation error.
isRequired
is the first validator to check, no matter in which order validators were attached.
rec.isValid( attrName )
Returns true
if the specified record's attribute is valid.
rec.getValidationError( attrName )
Return the validation error for the given attribute or null
if it's valid.
Record and Collection
Record and Collection share the same validation API. key
is the attribute name for the record and record's id/cid for the collection.
callback
obj.validate()
Override this method in subclass to define object-level validation rules. Whatever is returned from validate()
is treated as validation error.
obj.isValid()
Returns true
if the object is valid. Has same effect as !object.validationError
.
obj.isValid( key )
Returns true
if the specified record's attribute or collection element is valid. key
is an attribute's name for the record or record's id/cid for the collection.
obj.validationError
null
if an object is valid, or the the ValidationError object with detailed information on validation results.
ValidationError object has following shape:
{
error : /* as returned from collection.validate() */,
// Members validation errors.
nested : {
// key is an attrName for the record, and record.cid for the collcation
key : validationError,
...
}
}
obj.getValidationError( key )
Return the validation error for the given attribute or collection's item.
key
is an attribute's name for the record or record's id/cid for the collection.
obj.eachValidationError( iteratee : ( error, key, obj ) => void )
Recursively traverse aggregation tree validation errors. key
is null
for the object-level validation error returned by obj.validate()
.
obj
is the reference to the current object.
I/O and Serialization
Overview
Type-R implements generalized IO on top of the IOEndpoint
interface, with JSON serialization handled by Record and Collection classes.
IOEndpoint defines the set of CRUD + list methods operating on raw JSON.
Attachment of an endpoint to the record or collection enables I/O API. There are few endpoints bundled with Type-R, for instance memoryIO()
which can be used for mock testing.
@define class User extends Record {
static endpoint = memoryIO();
static attributes = {
name : '',
email : ''
}
}
const users = new User.Collection();
users
.add({ name : 'John' })
.save()
.then( () => console.log( user.id );
I/O API
static
endpoint
I/O endpoint declaration which should be used in Record or Collection definition to enable I/O API.
If an endpoint is defined for the MyRecord
, it's automatically defined for the corresponding MyRecord.Collection
as well.
attrDef
: Type.has.endpoint( endpoint
)
Override or define an I/O endpoint for the specific record's attribute.
obj.getEndpoint()
Returns an object's IO endpoint. Normally, this is an endpoint which is defined in object's static endpoint = ...
declaration, but it might be overridden by the parent's record using Type.has.endpoint( ... )
attribute declaration.
@define class User extends Record {
static endpoint = restfulIO( '/api/users' );
...
}
@define class UserRole extends Record {
static endpoint = restfulIO( '/api/roles' );
static attributes = {
// Use the relative path '/api/roles/:id/users'
users : User.Collection.has.endpoint( restfulIO( './users' ) ),
...
}
}
record.fetch( options? )
Asynchronously fetch the record using endpoint.read()
method. Returns an abortable ES6 promise.
An endpoint must be defined for the record in order to use that method.
record.save( options? )
Asynchronously save the record using endpoint.create()
(if there are no id) or endpoint.update()
(if id is present) method. Returns an abortable ES6 promise.
An endpoint must be defined for the record in order to use that method.
record.destroy( options? )
Asynchronously destroy the record using endpoint.destroy()
method. Returns an abortable ES6 promise. The record is removed from the aggregating collection upon the completion of the I/O request.
An endpoint must be defined for the record in order to use that method.
collection.fetch( options? )
Fetch the collection. Returns an abortable promise.
options
accepts an optional liveUpdates
parameter. When true
, collection subscribes for the live updates when I/O is finished.
collection.liveUpdates( true | false )
Subscribe for the live data updates if an I/O endpoint supports it (subscribe()
/unsubscribe()
IOEndpoint methods).
obj.hasPendingIO()
Returns an abortable promise if there's any I/O pending with the object, or null
otherwise.
Can be used to check for active I/O in progress or to abort pending I/O operation. Please note, that all pending I/O is aborted automatically when new I/O operation is started or an object is disposed. When I/O is aborted, the promise is rejected.
const promise = users.hasPendingIO();
if( promise && promise.abort ) promise.abort();
I/O endpoints
restfulIO( url, options? )
HTTP REST client endpoint. Requires window.fetch
available natively or through the polyfill. Implements standard BackboneJS REST semantic.
All I/O methods append an optional options.params
object to the URL parameters translating them to string with JSON.stringify()
.
record.save()
makes:POST url
, if the model has no id. Expects to receive{ id : recordId }
.PUT url/:id
, if the model has an id.
collection.fetch()
makesGET url
.record.destroy()
makesDELETE url
.
Supports URI relative to owner (./relative/url
resolves as /owner/:id/relative/url/:id
).
import { restfulIO } from 'type-r/endpoints/restful'
@define class Role extends Record {
static endpoint = restfulIO( '/api/roles' );
...
}
@define class User extends Record {
static endpoint = restfulIO( '/api/users' );
static attributes = {
// Roles collection here has relative url /api/users/:user_id/roles/
roles : Role.Collection.has.endpoint( restfulIO( './roles' ) ),
...
}
}
memoryIO( mockData?, delay? )
Endpoint for mock testing. Takes optional array with mock data, and optional delay
parameter which is the simulated I/O delay in milliseconds.
import { memoryIO } from 'type-r/endpoints/memory'
@define class User extends Record {
static endpoint = memoryIO();
...
}
localStorageIO( key )
Endpoint for localStorage. Takes key
parameter which must be unique for the persistent record's collection.
import { localStorageIO } from 'type-r/endpoints/localStorage'
@define class User extends Record {
static endpoint = localStorageIO( '/users' );
...
}
attributesIO()
Endpoint for I/O composition. Redirects record's fetch()
request to its attributes and returns the combined abortable promise. Does not enable any other I/O methods and can be used with record.fetch()
only.
It's common pattern to use attributesIO endpoint in conjunction with Store to fetch all the data required by SPA page.
import { localStorageIO } from 'type-r/endpoints/attributes'
@define class PageStore extends Store {
static endpoint = attributesIO();
static attributes = {
users : User.Collection,
roles : UserRole.Collection,
}
}
...
const store = new PageStore();
store.fetch().then( () => renderUI() );
proxyIO( RecordCtor )
Create IO endpoint from the Record class. This endpoint is designed for use on the server side with a data layer managed by Type-R.
Assuming that you have Type-R records with endpoints working with the database, you can create an endpoint which will use an existing Record subclass as a transport. This endpoint can be connected to the RESTful endpoint API on the server side which will serve JSON to the restfulIO endpoint on the client.
An advantage of this approach is that JSON schema will be transparently validated on the server side by the Type-R.
import { proxyIO } from 'type-r/endpoint/proxy'
...
const usersIO = proxyIO( User );
IOEndpoint Interface
An IO endpoint is an "plug-in" abstraction representing the persistent collection of JSON objects, which is required to enable records and collections I/O API. There are several pre-defined endpoints included in Type-R package which can be used for HTTP REST I/O, mock testing, working with localStorage, and IO composition.
You will need to define custom endpoint if you would like to implement or customize serialization transport for Type-R objects. Use built-in endpoints as an example and the starting boilerplate.
All IOEndpoint methods might return standard Promises or abortable promises (created with createIOPromise()
). An IOEndpoint instance is shared by all of the class instances it's attached to and therefore it's normally must be stateless.
endpoint.read( id, options, record )
Reads an object with a given id. Used by record.fetch()
method. Must return JSON wrapped in abortable promise.
endpoint.update( id, json, options, record )
Updates or creates an object with a given id. Used by record.save()
method when record already has an id. Must return abortable promise.
endpoint.create( json, options, record )
Creates an object. Used by record.save()
method when record does not have an id. Must return abortable promise.
endpoint.destroy( id, options, record )
Destroys the object with the given id. Used by record.destroy()
method. Must return abortable promise.
endpoint.list( options, collection )
Fetch an array of objects. Used by collection.fetch()
method. Must returns abortable promise.
endpoint.subscribe( callbacks
, collection )
Optional method to enable the live updates subscription. Used by collection.liveUpdates( true )
method. Must returns abortable promise.
Method callbacks
argument is an object of the following shape:
{
// Endpoint must call it when an object is created or updated.
updated( json ){}
// Endpoint must call it when an object is removed.
removed( json ){}
}
endpoint.unsubscribe( callbacks
, collection )
Unsubscribe from the live updates. Used by collection.liveUpdates( false )
method. Takes the same callbacks
object as subscribe()
.
createIOPromise( init )
Service function to create an abortable version of ES6 promise (with promise.abort()
which meant to stop pending I/O and reject the promise).
init
function takes the third onAbort
argument to register an optional abort handler. If no handler is registered, the default implementation of promise.abort()
will just reject the promise.
import { createIOPromise } from 'type-r'
const abortablePromise = createIOPromise( ( resolve, reject, onAbort ) =>{
...
onAbort( () => {
reject( 'I/O Aborted' );
});
});
Serialization
Record and Collection has a portion of common API related to the I/O and serialization.
obj.toJSON()
Serialize record or collection to JSON. Used internally by I/O methods. Can be overridden to customize serialization.
Produces the JSON for the given record or collection and its aggregated members. Aggregation tree is serialized as nested JSON. Record corresponds to an object in JSON, while the collection is represented as an array of objects.
If you override toJSON()
, it usually means that you must override parse()
as well, and vice versa.
@define class Comment extends Record {
static attributes = {
body : ''
}
}
@define class BlogPost extends Record {
static attributes = {
title : '',
body : '',
comments : Comment.Collection
}
}
const post = new BlogPost({
title: "Type-R is cool!",
comments : [ { body : "Agree" }]
});
const rawJSON = post.toJSON()
// { title : "Type-R is cool!", body : "", comments : [{ body : "Agree" }] }
option
{ parse : true }
obj.set()
and constructor's option to force parsing of the raw JSON. Is used internally by I/O methods to parse the data received from the server.
// Another way of doing the bestSeller.clone()
// Amazingly, this is guaranteed to work by default.
const book = new Book();
book.set( bestSeller.toJSON(), { parse : true } );
callback
obj.parse( json )
Optional hook called to transform the JSON when it's passes to the record or collection with set( json, { parse : true })
call. Used internally by I/O methods.
If you override toJSON()
, it usually means that you must override parse()
as well, and vice versa.
attrDef
: Type.has.toJSON( false )
Do not serialize the specific attribute.
attrDef
: Type.has.toJSON( ( value, name ) => json )
Override the default serialization for the specific record's attribute.
Attribute is not serialized when the function return undefined
.
attrDef
: Type.has.parse( ( json, name ) => value )
Transform the data before it will be assigned to the record's attribute.
Invoked when the { parse : true }
option is set.
// Define custom boolean attribute type which is serialized as 0 or 1.
const MyWeirdBool = Boolean.has
.parse( x => x === 1 )
.toJSON( x => x ? 1 : 0 );
static
create( attrs, options )
Static factory function used internally by Type-R to create instances of the record.
May be redefined in the abstract Record base class to make it serializable type.
@define class Widget extends Record {
static attributes = {
type : String
}
static create( attrs, options ){
switch( attrs.type ){
case "typeA" : return new TypeA( attrs, options );
case "typeB" : return new TypeB( attrs, options );
}
}
}
@define class TypeA extends Widget {
static attributes = {
type : "typeA",
...
}
}
@define class TypeB extends Widget {
static attributes = {
type : "typeB",
...
}
}
Normalized data
Type-R has first-class support for working with normalized data represented as a set of collections with cross-references by record id. References are represented as record ids in JSON, and being transparently resolved to record instances on the first access.
Store
class is the special record class which serves as a placeholder for the set of interlinked collections of normalized records. Id-references are defined as record attributes of the special type representing the serializable reference to the records from the specified master collection.
attrDef
: Record.from( sourceCollection
)
Serializable reference to the record from the particular collection.
Initialized as null
and serialized as record.id
. Is not recursively cloned, validated, or disposed. Used to model one-to-many relationships.
Changes in shared record are not detected.
sourceCollection
may be:
- the JS variable pointing to the collection singleton;
- the function returning the collection;
- the string with the dot-separated relative object path to the collection. It is resolved dynamically relative to the record's
this
. Following shortcuts may be used in path:owner.path
(or^path
) works as() => this.getOwner().path
.store.path
(or~path
) works as() => this.getStore().path
.
@define class State extends Record {
items : Item.Collection,
selected : Record.from( 'items' ) // Will resolve to `this.items`
}
attrDef
: Collection.subsetOf( sourceCollection
)
Serializable non-aggregating collection which is the subset of the particular collection. Serialized as an array of record ids. Used to model many-to-many relationships.
The collection itself is recursively created and cloned. However, its records are not aggregated by the collection thus they are not recursively cloned, validated, or disposed.
sourceCollection
is the same reference as used by Record.from()
.
@define class Role extends Record {
static attributes = {
name : String,
...
}
}
@define class User extends Record {
static attributes = {
name : String,
roles : Role.Collection.subsetOf( '~roles' )
}
}
@define class UsersDirectory extends Store {
static attributes = {
roles : Role.Collection,
users : User.Collection // `~roles` references will be resolved against this.roles
}
}
collection.createSubset( records?, options? )
Create an instance of Collection.subsetOf( collection )
type (non-aggregating serializable collection) which is the subset of the given collection. Takes the same arguments as the collection's constructor.
class
Store
Store
is the special kind of record which serves as a root for id references.
For all records inside of the store's aggregation tree ~attrName
will resolve to the attribute of the store class found with record.getStore()
method. If there are no such an attribute in the store, the next available store upper in aggregation tree will be used (as regular records stores can be nested), or the default store if there are no one.
Store is the subclass of the Record. It's defined extending the Store
abstract base class. It behaves as a regular record in most aspects.
store._defaultStore
Reference to the master store used for lookups if the current store doesn't have the required attribute and there are no other store found upper in the ownership chain.
Defaults to the Store.global
. May be explicitly defined to create custom store lookup chains across the ownership hierarchy.
static
Store.global
The default singleton store class. Is always the last store to lookup when resolving ~reference.
Use the default store for the globally shared data only. Each application page must have its local store.
@define class MyStore extends Store {
static attributes = {
users : User.Collection,
roles : Role.Collection
}
}
Store.global = new MyStore();
// Now the reference '~users` will point to users collection from the MyStore.
recordOrCollection.getStore()
Return the closest store. Used internally to resolve symbolic ~reference
relative to the store.
Method looks for the Store
subclass traversing the ownership chain of current aggregation tree upwards. If there are no store found this way, default Store from Store.global
is returned.
recordOrCollection.clone({ pinStore : true })
Make the cloned object to preserve the reference to its original store.
Cloned objects don't have an owner by default, thus they loose the reference to their store as no ownership chain can be traversed. pinStore
option should be used in such a cases.
ReactJS bindings
React-MVx is an MVVM application frameworks which uses Type-R to manage a multi-layer application state.
- React-MVx adds two-way data binding capabilities to both Record and Collection. Data-binding is based on the "value link" pattern and implemented with Link class from NestedLink library taking the value and validation error directly from the Record.
- Record class is used to manage component's state.
- The subset of the Record attributes type annotation is used to define component props and context.
- Store class is used to represent component's store (also called "local store"), which is typically used in the root component of SPA page associated with a route. The state of the children components uses upper component's stores to resolve id-references.
- Type-R global store may be used as a store for the data shared by application pages. This store is used to resolve id-references in case if local stores lookup failed.
Class definitions and mixins
Class Definitions
Type-R mechanic is based on class transformations at the moment of module load. These transformations are controlled by definitions in static class members.
decorator
@definitions({ propName : rule
, ... })
Treat specified static class members as definitions. When @define
decorator is being called, definitions are extracted from static class members and mixins and passed as an argument to the Class.onDefine( definition )
.
Class definitions are intended to use in the abstract base classes and they are inherited by subclasses. You don't need to add any new definitions to existing Type-R classes unless you want to extend the library, which you're welcome to do.
rule
mixinRules.value
Merge rule used to mark class definitions. The same rule is also applied to all mixin members if other rule is not specified.
@define
@definitions({
urlRoot : mixinRules.value
})
class X {
static urlRoot = '/api';
static onDefine( definition ){
this.prototype.urlRoot = definition.urlRoot;
}
}
rule
mixinRules.protoValue
Same as mixinRules.value
, but the value is being assigned to the class prototype.
@define
@definitions({
urlRoot : mixinRules.protoValue
})
class X {
static urlRoot = '/api';
}
assert( X.prototype.urlRoot === '/api' );
rule
mixinRules.merge
Assume the property to be the key-value hash. Properties with the same name from mixins are merged.
const M = {
attributes : {
b : 1
}
};
@define
@mixins( M )
@definitions({
attributes : mixinRules.merge
})
class X {
static attributes = {
a : 1
};
onDefine( definitions ){
const { attributes } = definitions;
assert( attributes.a === attributes.b === 1 );
}
}
decorator
@define
Extract class definitions, call class definition hooks, and apply mixin merge rules to inherited class members.
- Call static
onExtend( BaseClass )
hook. - Extract definitions from static class members and all the mixins applied, and pass them to
onDefine( definitions, BaseClass )
hook. - Apply merge rules for overriden class methods.
All Type-R class definitions must be precedeed with the @define
(or @predefine
) decorator.
@define
@definitions({
attributes : mixinRules.merge
})
class Record {
static onDefine( definitions, BaseClass ){
definitions.attributes && console.log( JSON.stringify( definitions.attributes ) );
}
}
// Will print "{ "a" : 1 }"
@define class A extends Record {
static attributes = {
a : 1
}
}
// Will print "{ "b" : 1 }"
@define class B extends Record {
static attributes = {
b : 1
}
}
decorator
@define( mixin )
When called with an argument, @define
decorator applies the given mixin as if it would be the first mixin applied.
In other aspects, it behaves the same as the @default
decorator without argument.
static
Class.onExtend( BaseClass )
Called from the @predefine
or as the first action of the @define
. Takes base class constructor as an argument.
static
Class.onDefine( definition, BaseClass )
Called from the @define
or Class.define()
method. Takes class definition (see the @definitions
decorator) as the first argument.
decorator
@predefine
The sequence of @predefine
with the following Class.define()
call is equivalent to @define
decorator. It should be used in the case if the class definition must reference itself, or multiple definitions contain circular dependency.
It calls static onExtend( BaseClass )
function if it's defined. It assumes that the Class.define( definitions )
method will be called later, and attaches Class.define
method to the class if it was not defined.
static
Class.define( definitions? )
Finalized the class definition started with @predefine
decorator. Has the same effect as the @define
decorator excepts it assumes that Class.onExtend()
static function was called already.
Mixins
decorator
@mixins( mixinA, mixinB, ... ) class X ...
Merge specified mixins to the class definition. Both plain JS object and class constructor may be used as mixin. In the case of the class constructor, missing static members will copied over as well.
import { mixins, Events } from 'type-r'
...
@define
@mixins( Events, plainObject, MyClass, ... )
class X {
...
}
static
Class.mixins
Class member holding the state of the class mixins.
Merge rules
decorator
@mixinRules({ propName : rule
, ... })
The rule
is the reducer function which is applied when there are several values for the particular class members are defined in different mixins or the class, or if the class member is overriden by the subclass.
rule
mixinRules.classFirst
Assume the property to be the function. Call functions from mixins in sequence: f1.apply( this, arguments ); f2.apply( this, arguments );...
rule
mixinRules.classLast
Same as sequence, but functions are called in the reverse sequence.
@define
@mixinRules({
componentWillMount : mixinRules.classLast
})
class Component {
componentWillMount(){
console.log( 1 );
}
}
const M = {
componentWillMount(){
console.log( 2 );
}
}
@define
@mixins( M )
class X extends Component {
componentWillMount(){
console.log( 3 );
}
}
const x = new X();
x.componentWillMount();
// Will print 1, 2, 3
rule
mixinRules.pipe
Assume the property to be the function with a signature ( x : T ) => T
. Join functions from mixins in a pipe: f1( f2( f3( x ) ) )
.
rule
mixinRules.defaults
Assume the property to be the function returning object. Merge objects returned by functions from mixins, executing them in sequence.
rule
mixinRules.every
Assume property to be the function returning boolean. Return true
if all functions from mixins return truthy values.
rule
mixinRules.some
Same as every
, but return true when at least one function from mixins returns true.
Release Notes
2.1.0
This release adds long-awaited HTTP REST endpoint.
- IO endpoints moved outside of the man sources tree. Creation of the custom endpoints is easier than ever.
- Added HTTP REST endpoint
restfulIO
with relative urls support (https://volicon.github.io/Type-R/#endpoint-restfulio-url-options-). - Added proxyIO endpoint for creating endpoints from records on the server side (https://volicon.github.io/Type-R/#endpoint-proxyio-recordctor-).
2.0.0
This release brings new features which fixes problems with component's inheritance in React bindings and implements long-awaited generic IO implementation based on ES6 promises.
There shouldn't be breaking changes unless you're using custom logger or React bindings (formerly known as React-MVx, with a name changed to React-R in new release).
Generic IO support
New IOEndpoint concept is introduced, making it easy to create IO abstractions. To enable Record
and Collection
IO API, you need to assign IO endpoint in the class definition.
Endpoint is the class defining CRUD and list operations on JSON data, as well as the methods to subscribe for the data changes. There are two endpoints included with 2.0 release, memoryIO
which is suitable for mock testing and localStorageIO
which could be used in demos and prototypes. They can be used as a references as starting points to define your own IO endpoints.
@define class User extends Record {
static endpoint = memoryIO();
static attributes = {
name : String,
...
}
}
There are three Record IO methods (save()
, fetch()
, and destroy()
) and two collection IO method (fetch()
and liveUpdates()
) ). All IO methods returns ES6 promises, so you either must have the runtime supporting ES6 or use the ES6 promise polyfill. The promises are modified to be abortable (all of them have abort()
method).
const user = new User({ name : 'John' });
user.save().then( () => {
console.log( `new user is added ${ user.id }` )
});
There's the special attributesIO()
endpoint to fetch all of attributes independently and return the combined promise. This is the recommended way of fetching the data required by SPA page.
@define class PageStore extends Store {
static endpoint = attributesIO();
static attributes = {
users : User.Collection,
roles : UserRole.Collection,
...
}
}
const store = new PageStore();
store.fetch().then( () =>{
// render your page
});
It's possible to define or override the defined endpoint for the nested model or collection using has.endpoint
type-R attribute annotation.
@define class PageStore extends Store {
static endpoint = attributesIO();
static attributes = {
users : User.Collection.has.endpoint( restful( '/api/users' ) ),
roles : UserRole.Collection.has.endpoint( restful( '/api/userroles' ) ),
...
}
}
New mixins engine
Type-R metaprogramming system built on powerful mixins composition with configurable member merge rules. In 2.0 release, mixins engine was rewritten to properly apply merge rules on inheritance. This feature is heavily used in Type-R React's bindings and is crucial to prevent errors when extending the React.Component
subclasses.
An example illustrating the principle:
@define
// Define the class with
@mixinRules({
componentWillMount : mixinRules.classLast,
componentWillUnmount : mixinRules.classFirst
})
class Component {
componentWillMount(){
console.log( 1 );
}
componentWillUnmount(){
console.log( 3 );
}
}
@define
@mixins({
componentWillMount(){
console.log( 2 );
},
componentWillUnmount(){
console.log( 2 );
}
})
class MyBaseComponent extends Component {
componentWillMount(){
console.log( 3 );
}
componentWillUnmount(){
console.log( 1 );
}
}
In this example, all of the methods defined in the mixin, base class, and subclass will be called in the order specified in the console.log
.
Other changes
- Update pipeline was rewritten to improve record's initialization speed (collection's fetch speed is improved by 30%).
- Fixed bug which case dynamic type checks to be disabled in records constructors.
- New implementation of the
Collection.subsetOf
which both fixes some edge case bugs and is more efficient. - New logger handling NODE_ENV variable setting.


The chromodo browser disables the same origin policy, use this page to test.
https://www.comodo.com/home/browsers-toolbars/chromodo-private-internet-browser.php
Domain you would like to access:
Code you would like to run:
Output will appear here:


Interface: McuPowerOverride
interface McuPowerOverride
Interface to allow high-level components to set a lower bound for a
microcontroller's low power state. This is sometimes necessary,
e.g., if a very low power state has a long wakeup latency that will
violate application timing requirements. TEP 112 describes how
TinyOS incorporates this information when the Scheduler tells
the MCU to enter a low power state.
- Author:
-
Philip Levis
- Date:
-
Oct 26, 2005
- See:
-
TEP 112: Microconroller Power Management
Commands
command mcu_power_t lowestState()
Commands - Details
lowestState
command mcu_power_t lowestState()
- Returns:
-
the lowest power state the system can enter to meet the
requirements of this component


Fixed every indentation error in the HTML and pushed changes to the GitHub account.
Basic html website about my favorite place and things.
Build a Wikipedia web page with Bootstrap Components and Grid System
BSC in Computer Science and Engineering.
East West University Of Bangladesh.
Epicodus program appears to be the right fit for me to gain additional skill in coding.
I always enjoy Gardening that's make me active.
Adventure and traveling also lets me open new chances and possibilities
I enjoy cooking with my family.I love doing ski on weekend.


A Note from CESE Director, Jackie Bortiatynksi
As active members in the Center for Excellence in Science Education faculty community I am calling on all of you. First, CESE's leadership wants to say thanks for all that I know you are currently doing to help support our colleagues in transitioning their teaching format to an online environment. Second, I want to ask that you help in the dissemination of information on resources the ECoS Office of Digital Learning is sharing with all of our faculty. I know some of you are creating resources for your home departments and are already currently with Melissa and her team but if you have not shared these materials with them I urge you to do so soon. Also providing feedback to the CESE leadership team (Jennelle, Jackie C., Lynne and me) on how we can be of support to your colleagues would be great. We will be continuing with our "Food for Thought" brown bag series in a virtual format but there may be other virtual sessions that we can host to help support faculty. CESE has become one of the important hubs of faculty community and so I want to leverage that strength to help in getting information and expertise and helping to get it dispersed. All of you are the extensions of CESE in this College. Please let me know how CESE can be of support and please feel free to send me ideas anytime.
Take care and stay healthy,
Jackie
CESE WebsiteA task group of faculty, staff, and administrators is currently developing specific guidelines for teaching remotely and in-person during the fall 2020 semester. The task group is taking active steps to engage the Penn State teaching community in this process.
Information on fall 2020 delivery, teaching and testing, technology training, and many other questions are provided.
For more information, visit: keepteaching.psu.edu/fall-2020/
Joe Houck of Chemistry has created and shared a number of resources. While they may be related to Chemistry, many can be modified to fit your needs:
Here are some videos he made and shared within the Chemistry department. There is one video on creating a link and sharing it with students and another is an overview of Zoom features that he shared with Learning Assistants.
Requiring Webcams When considering the use of webcams in courses offered through remote learning technologies, there are three main factors to consider and balance:
Academic integrity, integrity of assessment, Student privacy, Student access to technology and resources, etc.
To balance between these factors, the following guidance has been developed.
All students are expected to behave with respect in class, no matter the format or delivery mode of the class. If you are disruptive in class (face-to-face or remote), your instructor may speak to you about your behavior. If your behavior does not change, you may be asked you to leave the class. If your behavior continues to be disruptive, a report can be filed with the Office of Student Conduct and your behavior will be responded to through the conduct process. Students involved in a serious disruption of the learning environment may not be permitted to return to class until University procedures have been completed.
The Office of Student Conduct provides guidance for how faculty can respond to student disruption. The office refers faculty to a conduct report on their website and they're also available to arrange conversations between faculty members and case managers to discuss details in-person.
Email<EMAIL_ADDRESS>File a report: Office of Student Conduct
For information on Classroom Conduct during COVID-19, visit: studentaffairs.psu.edu
The 'Keep Learning' website also provides a number of resources for students coming into the fall 2020 semester. The FAQ section explores areas of advising, class formats / instructional modes, health and saftey, and general expectations outlining disruption and accountability.
Visit: keeplearning.psu.edu/fall-2020/frequently-asked-questions/
The following publishers have made additional resources to faculty who are using their text or online resources. Contact your rep for more information:
Barnes & Noble Education joins VitalSource and leading publishers to make eTextbooks free for students impacted by COVID-19 campus closures; read more.


Explaining child birth correctly involves an explanantion of the reproductive process in Humans (or generally, sexual reproduction in all species who do so). Doing so would require parents, or any adult for that matter, to talk about the reproductive organs in the body, which can get awkward. But is it morally correct or not?
Yesterday, I, an atheist Hindu, and my father, a theist Hindu, were having a discussion about the existence of God and religion in the Hindu (south Asian) culture. I argued that Hindus (whatever that means), especially the ones following the "Sanatana Dharma", never believed in the existence of an Almighty, until they were ruled by foreigners who believed so. And my father argued that masses cannot understand the complex constructs that I am talking about and that a culture with a belief in a almighty is required for masses to follow it.
During this discussion, he brought up a really interesting point. He asked me what would I tell my children when they ask about rainfall and child birth? When I started to tell that I would tell them the truth, he responded with how much truth? And that the child would never stop asking questions unless there is a strong answer, like "The Almighty does it by will", which invites no further questions. He said that it is much wiser to answer with "There is a giant elephant who showers the water in the form of rain" or "children are gifts given to couples when they have been good as children". He argued that it is important to appease the curiosity of the child, otherwise you would never get anything done, and the child might go mad in search of answers.
Apparently, my thoughts are the polar opposites of those. I felt that it is a great boon to have undying curiosity. The world is a complex place, and we, as the humanity, do understand a lot of it. So why misguide our children into thinking that things are a certain way while they actually aren't. The way physics works is beyond what we can decipher even scientifically. So why waste the time of the young humans in learning a baseless explanation of natural activities, then unlearn them and learn the most updated scientific explanations of those? Don't we want them to be able to figure out where the knowledge gap is and try to figure out what an even better explanation would be?
My plan to deal with such questions with my children is to tell them the most updated scientific truth. They might get too knowledgeable about reproductive organs much sooner than all their peer, and that might lead to some awkward and downright embarrassing situations. But I think that is a smaller price to pay compared to the volume of maturity that it will bring to my children. We live in the information age. As soon as they learn to read and write, I will expose them to Wikipedia. I will nurture a more scientific way of living in them, while abiding by all the formailities, rituals and customs of our traditional Hindu culture.


The objective of this project is to create an autonomous mobile robot that follows the track. An Android smartphone is used as a camera and the interactive user interface. The phone also sends information to a PIC32 microchip to control the motors' steering wheels. A user can adjust the tracked color threshold through the Android application on the phone.
The chassis was designed using Onshape and were constructed using acrylic sheets. The acrylic sheets were cut to shape using a laser cutter and a few parts such as the motor mounts were 3d printed since they are more durable.
The arduino was connected to the PIC32 via USB CDC and the an application was designed were the camera reads the center of the line and sends back the co-ordinates to the PIC32.
The PIC32 reads the co-ordinates and then sends a pwm signal to the motor which in turn centers the robot along the center of the line. The robot was battery operated with a Lipo battery.


{{selectedDateGoal}}
Total goal sales set for the day.
{{selectedDateSales}}
{{achievementPercentage}}%
Total actual sales made on the day.
{{selectedDateSalesTeam}}
Number of Salesperson deployed on the day.
{{selectedDateReturns}}
Number of crates returned on the day.
G:{{salespersonGoal}} S:{{salespersonSold}} P:{{salespersonPercentage}}%
No Data Available


by Max Scheinin Read more of Max's reviews at http://www.garfieldlib.com/yanews/july97/max/max.html
In 1989, Tim Burton took the legendary comic book figure of Batman and turned him into a huge box office hit with an atmospheric little film that created an utterly distinct feeling somewhere between film noir and The Godfather. Was it "deep"? Nope. However it was a perfect entertainment and the audience ate it up. It was natural that Roger Ebert trashed it writing that it was "without the comic book uplift" of the Indiana Jones and Superman pictures. Which was true. If what you were looking for was the tale of a heroic man's escadapes and adventures you were looking in the wrong place. If those films were Tom Sawyer, then Batman was Huckleberry Finn. Some critics ( Glenn Lovell of The San Jose Mercury News and Peter Travers of Rolling Stone ) realized that it was something special; Mr. Lovell gave it ***1/2 and wrote that "This is Batman as Batman should be."; Mr. Travers called it one of the ten best films of the year. The praise was deserved - from start to finish the film was enthralling, exciting, and superb in every respect. There was no doubt that there would be a sequel directed by Burton.
The result was Batman Returns which forgets that it wants to be fun within the first five minutes. The villains ( the hideous Penguin and seductive Catwoman ) take over, and our epectations are always left unfulfilled - when we wanted the Penguin to bite off Batman's nose he simply gets mad, and when we want Catwoman to slither over Batman's body she gets pushed off a building. I admire Mr. Burton for trying to do more then entertain, but the result is a mess. Nothing works out as planned - it fails both to entertain and to enlighten. Hints are made to the audience that this film could be a blast. Alas, these are only hints. There are, don't misunderstand me, several moments of cinematic genuis contained; Catwoman and Batman understand eachother perfectly without a word on the subject being said; Christopher Lloyd is trapped, by the Penguin, within a giant cage; a small basket is dropped into a large river; but nothing is drawn together and Burton is unable to make the film work on any regular basis. A weak followup, both career and sequel wise, Batman Returns is sometimes dazzling, but to often dissapointing.
Grade: D+
The review above was posted to the
rec.arts.movies.reviews newsgroup (de.rec.film.kritiken for German reviews).
The Internet Movie Database accepts no responsibility for the contents of the
review and has no editorial control. Unless stated otherwise, the copyright
belongs to the author.
Please direct comments/criticisms of the review to relevant newsgroups.
Broken URLs inthe reviews are the responsibility of the author.
The formatting of the review is likely to differ from the original due
to ASCII to HTML conversion.
Related links: index of all rec.arts.movies.reviews reviews


Sometimes things that are visible are not the reality and the things that are the reality are always hidden.


DADI Music Store
Search
Login
Log Out
Cart
Search for Songs
Search for a song
Genre:
Title
Album
Genre
Artist
Add to Cart
Add


Pizza Party RSVP
Colony Park Pizza Party featuring POMPEII’S INFERNO PIZZA
Please come out and join the fun, food and getting to know more of your neighbors!
- Where: Colony Park Park
- When: Sunday, September 20, 2015 from Noon – 3:00 p.m. rain or shine
- Why: It will be fun and tasty and the proceeds will go towards other events and maintenance of Colony Park entrance
- Cost: $1.00/per ticket; each food item will be one ticket
There will be pizza, hot dogs, chips, beverages, and sweet treats to eat, plus face painting and games. Tickets can be purchased at the event. There will also be a gift basket being raffled off, must be present to win. Please bring your own chair.


Rock Paper Scissors
Username:
Opponent:
Opponent's Stance:
Messages
Player Messages
messages will be displayed here.


VARK Learning Style: Multimodal Learning Preference (Type 1)
Going into Dev Bootcamp, I took the VARK questionnaire to see what my learning style is. Not surprisingly, the survey concluded that I had a "Multimodal Learning Preference"- not favoring any one mode in particular. It pointed out that I change my primary approach to learning depending on the situation- classifying me as "Type 1". Here is the breakdown:
- Visual 5 The visual modality includes maps, diagrams, charts, graphs, and other visual representation of information, excluding still pictures, movies, or other designs. Initially, I was surprised that this didn't dominate my VARK style, as I consider myself a very visual and creative person. Now having a few weeks of Phase 0 under my belt, it seems more accurate- but I still suspect this may only be circumstantial.
- Aural 4 A mode primarily focused on the spoken word- aural learners thrive in lectures, discussion, radio, email, or even web-chat. My least preferred learning modality (according to the quiz!), I ask myself why this is less effective than the others. My conclusion after reflecting on the last three weeks is that aural learning is best suited for me for non-technical topics of interest. I listen to a lot of podcasts, watch lectures online, and enjoy compelling conversation with friends or strangers- actually these are some of my greatest day to day pleasures in life! But for more technical learning, such as Dev Bootcamp, the details that have to be retained (such as html or css syntax) need to be read, written, and repeated for optimum learning. The fact that I can see the circumstantial differences in my learning styles only further confirms my multimodal nature!
- Read/Write 6 Already knowing that reading and writing are imperative to my learning process, I started Phase 0 diligently taking notes. I consolidated the most important pieces of curriculum from Github, recommended readings, Codeacademy, and stack overflow for a go-to reference guide. Additionally, I found writing and reviewing flashcards for command line really helpful.
- Kinesthetic 6 And finally, kinesthetic mode- according to the VARK website, "includes demonstrations, simulations, videos and movies of 'real' things, as well as case studies, practice and applications." Equally weighted with reading and writing, real life experiences have actually been equally beneficial to me in DBC. I've been meeting regularly in-person with other students already in San Francisco providing a coworking environment where we can not only ask questions as we work, but compare notes, so to speak, on our various challenges. In one of my pairing sessions, my partner explained a concept to me via pseudocoding with pen and paper. It was memorable and helped me later when I needed to recall the method. These unique experiences help me distinguish day upon day of learning on my laptop. I expect this will continue to prove itself over again throughout the program.
Now wrapping up Week 3 of Phase 0, I feel a lot of validation in my initial predictions for what I would need to do to help me learn the curriculum. Knowing that technical learning is more difficult for me than creative pursuits, I reached out to other students early and often- proving to reinforce my kinesthetic learning. Another behavior that has proven worthwhile is the extra time and effort in notes and flashcards. Because syntax has been the most challenging part of the curriculum to retain, the repetitive nature of reading, writing, and recalling seems to be the only thing that can turn what I've memorized into real knowledge.
The insight I've gained from understanding my learning style is very much aligning with my mindset. In my first blog, I discussed growth versus fixed mindset. I still feel comfortable acknowledging when I am struggling with the curriculum. Some days, I seem to be asking all of the questions in my cowork environment, but other days the roles reverse and I feel I have a more solid grasp on the material than my peers. I'm happy to know where my weaknesses lie so that I can improve upon them. Likewise, the feedback I've received to date regarding my technical contributions to peer-pairing has been less than overwhelming. This is clearly an opportunity for me to step up my efforts so that I can be as helpful, if not more, to my partners as they have been to me. Afterall, my mother taught me that it's rude not to reciprocate- my Southern values will triumph afterall! Tomorrow starts a new week.


To print a remote file, select (highlight) the file that you would like to print and do any one of the following:
Remote File -> print
menu item from
the menu bar.
print
from the context menu
Ctrl + P
(Command + P
on Mac
OS X)
When you do any of the above actions, the file will be opened for printing using the associated application.
The
remote file will first be downloaded to a temporary location before it
can be printed. Printing files using the associated applications is
not supported on some operating systems, in which case, the
Print
menu item will be grayed out.


{% extends "temps/index.html" %}
{% load static %}
{% block title %}Django Ajax CRUD{% endblock %}
{% block content %}
Since opening in 2000, we have become masters of our craft. Our commitment to quality products, exceptional services and incomparable customer care keep our community coming back again and again. We never stop improving, and are continuing to expand our offerings based on how we can best serve the San Francisco area. Since opening in 2000, we have become masters of our craft. Our commitment to quality products, exceptional services and incomparable customer care keep our community coming back again and again. We never stop improving, and are continuing to expand our offerings based on how we can best serve the San Francisco area. Since opening in 2000, we have become masters of our craft. Our commitment to quality products, exceptional services and incomparable customer care keep our community coming back again and again. We never stop improving, and are continuing to expand our offerings based on how we can best serve the San Francisco area.
More About Gresod


§264-63 Acquisition of property. For the purposes of this part, the director of transportation may acquire for controlled-access facilities and service roads:
(1) Property, as defined in chapter 101, by gift, devise, or purchase; or
(2) Property by eminent domain under chapter 101. [L 1960, c 3, pt of §2; Supp, §111-46; HRS §264-63]


Our Locations
Seattle
Location: 113 Cherry St, Seattle, WA 98104-2205, US
Working Hours: 6:00 am - 8:00 pm
Location: 113 Cherry St, Seattle, WA 98104-2205, US
Working Hours: 6:00 am - 8:00 pm
Tokyo
Location: Kyobashi MID Bldg., 13-10, Kyobashi 2-chome, Tokyo
Working Hours: 6:00 am - 8:00 pm
Location: Kyobashi MID Bldg., 13-10, Kyobashi 2-chome, Tokyo
Working Hours: 6:00 am - 8:00 pm
Dubai
Location: Sheikh Mohammed Bin Rashed Boulevard Downtown , Dubai
Working Hours: 6:00 am - 8:00 pm
Location: Sheikh Mohammed Bin Rashed Boulevard Downtown , Dubai
Working Hours: 6:00 am - 8:00 pm
Paris
Location: 172 boulevard Saint Germain, 75006 Paris
Working Hours: 6:00 am - 8:00 pm
Location: 172 boulevard Saint Germain, 75006 Paris
Working Hours: 6:00 am - 8:00 pm
Lima
Location: Plaza de Armas, Sacred Valley of the Incas, Lima
Working Hours: 6:00 am - 8:00 pm
Location: Plaza de Armas, Sacred Valley of the Incas, Lima
Working Hours: 6:00 am - 8:00 pm


Paul's Asian Food
老北京
Home
Buffet
Lunch Special
Full Menu
Direction
320 4th St NE, Devils Lake, ND 58301<PHONE_NUMBER>


--- layout: page title: Write for Us tags: [] status: publish type: page published: true meta: _edit_last: '2' _wp_page_template: default _aioseop_title: Write for Missenchanted _aioseop_description: Write for Missenchanted and get paid. _aioseop_keywords: pay for post,pay for blog, write for beauty blog _yoast_wpseo_linkdex: '0' ---
Welcome to MissEnchanted...
We are looking for enthusiast writers who can contribute to Missenchanted.
Please drop me a mail at<EMAIL_ADDRESS>I shall reply back to you with more details.


{% include "layouts/analytics.html" %} {% include "layouts/log.html" %} {% block css %} {% endblock %}


Class: tweak.EventSystem
| Defined in: | app\lib\events.coffee |
Overview
The framework is driven by a powerful and simple event system.
The event system simply stores the callbacks in an object tree. The tree allows quick traversal of events, during interaction with the event API
Variables Summary
- events =
-
{}
Initialise the event object
Instance Method Summary
- # (Boolean) on(context, name, callback, maxCalls = null) Add an event listener into the event system.
- # (Boolean) off(context, name, callback) Remove from events by given name
- ~ (Boolean) trigger(name, params) Trigger events by name
- # (Event, Null) find(name, build = false) Iterate through the events to find given event
- # (void) reset() Resets the events back to empty.
Constructor Details
#
(void)
constructor()
Private
Instance Method Details
#
(Boolean)
on(context, name, callback, maxCalls = null)
Add an event listener into the event system. The event system will store the object based on its event name
Examples:
Sample Event (JS)
tweak.Events.on(this, "sample/event", function(){
alert("Sample event triggered.")
})
Sample Event (CoffeeScript)
tweak.Events.on @, "sample/event", ->
alert "Sample event triggered."
Sample Event with Max Calls (JS)
// This example will allow the event to be called twice
// The event is automatically removed from the event object on max calls
tweak.Events.on(this, "sample/event", function(){
alert("Sample event triggered.")
}, 2)
Sample Event (CoffeeScript)
// This example will allow the event to be called twice
// The event is automatically removed from the event object on max calls
tweak.Events.on(@, "sample/event", ->
alert "Sample event triggered."
,
2)
#
(Boolean)
off(context, name, callback)
Remove from events by given name
~
(Boolean)
trigger(name, params)
~
(Boolean)
trigger(obj, params)
Trigger events by name
#
(Event, Null)
find(name, build = false)
Iterate through the events to find given event
#
(void)
reset()
Resets the events back to empty.


Ada mode with Ada_Semantic_Tree.Std_Entities; with Ada_Semantic_Tree.Generics; package body Ada_Semantic_Tree.Assistants is procedure Register_Ada_Assistants (Db : Construct_Database_Access; Std_Entities_Files : Virtual_File) is begin Ada_Semantic_Tree.Cache.Register_Assistant (Db); Ada_Semantic_Tree.Generics.Register_Assistant (Db); Ada_Semantic_Tree.Interfaces.Register_Assistant (Db); Ada_Semantic_Tree.Std_Entities.Register_Assistant (Db, Std_Entities_Files); end Register_Ada_Assistants; end Ada_Semantic_Tree.Assistants; MIME types defined: text/x-ada.


Go back to list of events
Update Event
Event Name
Event Description
Address
Price
Image
Image file name
cannot
contain any accented letters or symbols. Only use letters, numbers, spaces, underscores.
Start Date
TIMES ARE IN UTC TIMEZONE (WHICH IS +4 hours from our current time zone).
Do not update dates without Kevin. It is a very touchy value!!
End Date
Format: See above
Mood
Example: "1" or "1, 2"
1 = Energetic (RED)
2 = Active (ORANGE)
3 = Fun (YELLOW)
4 = Calm (BLUE)
Make sure the numbers are in the
range [1-4]
seperated by
commas
Hashtags
Example: "#bbq, #food, #outdoor, #summer, #music"
Make sure each hashtag
begins with #
seperated by
commas
*Please fill in one of the following: Website, Phone Number of Email*
Website
Phone Number
Format:<PHONE_NUMBER>
Email Address
Loading