Getting started
Type-R is the modern JS data framework allowing declaratively definitions of complex domain and UI application state. The state is defined as the superposition of JS classes extending Record
and Collection
, and has following features:
- It's mapped to JSON by default. The mapping can handle sophisticated scenarios with nested JSON and relations by id, and can be easily customized for every particular attribute or class.
- All changes are observable, happens in the scope of transactions, and there's the fine-grained change events system.
- Validation is performed on the first access to the validation error and never happens twice for unchanged data.
- Everything is typed at run-time and is protected from improper updates. The shape of generated JSON and data classes is guaranteed to match the definitions.
- It still looks like regular JS classes and is freaking fast. Type-R data structures are about 10 times faster than Backbone models and collections.
Data layer is defined as a superposition of three kinds of building blocks:
- Record classes with typed attributes.
- Ordered collections of records.
- Stores are records with a set of collections in its attributes used to resolve id-references in JSON.
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 superior to Ember Data in its capabilities and an order of magnitude faster than Backbone.
The closest things to the Type-R are Ember Data, BackboneJS models and collections, and mobx.
There are a lot of similarities:
- All that things can be used to manage an application's state.
- Type-R handles observable transactional changes in the way more or less similar to mobx (however, it's heavily optimized for the case of large aggregated object trees).
- Type-R id-relationships and validation capabilities are comparable to ones in Ember Data, which was used a source of inspiration.
- Type-R has a lot of common in some API parts with BackboneJS models and collections. For instance, it uses the close API for the collection updates and the similar event model for the changes (however, it's generalized for the case of transactions on the nested records and collections).
Speaking of the distinguishing differences,
- Type-R's records are not key-value pairs but classes with typed attributes. They are protected from improper assignment with run-time type assertions and conversions. Which means that the client-server protocol is protected again errors from both ends.
- 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. - Type-R relies on 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 features automatic lazily evaluated validation with declarative attribute-level rules. Validation checks are the part of the attribute type; they are evaluated in the moment they needed and never performed twice on the unchanged data.
- Type-R is really fast. It's capable of handling collections of 10K elements with real-time response and is about 10 times faster than BackboneJS.
| 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 | ✓ | - | ✓ | - |
Events
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 mixin
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
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.
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. |
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
.
In contrast to the "model" class in the majority of data frameworks, Record is not the key-value hash. It's the class with statically defined set of attributes of known types.
Record
itself is an abstract class. The subclass needs to be defined for every data structure of different shape, in a similar way as it's done in statically typed languages.
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
Define
Record definition must:
- be the class extending the
Record
; - be preceded with the
@define
decorator; - have
static attributes
definition.
decorator
@define
Must be placed before record 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.
attributes : {
text : String,
replies : Comments
}
});
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.
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_.
}
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 singleton collection;
- 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
.^
symbol in path is being translated to thegetOwner()
call.
@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
may be the same as for Record.from()
.
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.
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
}
});
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.
// 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.
Change events
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;
}
}
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.
Serialization
record.toJSON()
Produces the JSON for the given record 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.
May be overridden in the particular record class.
@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" }] }
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
.
option
{ parse : true }
record.set
and constructor's option to force parsing of the raw JSON.
Must be used to process 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
record.parse( json )
Invoked internally when { parse : true }
option is passed. May be overridden to define custom JSON transformation. Should not be called explicitly.
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",
...
}
}
Validation
Validation happens transparently on the first access to any part of the validation API. Validation results are cached. Only the required parts of aggregation tree will be validated again
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.
callback
record.validate()
Override in the subclass to add object-level validation. Whatever is returned from validate()
is treated as an error message and triggers the validation error.
record.isValid( attrName? )
Returns true
whenever the record and all of its attributes are valid.
If attrName
is provided, returns true
whenever the record's attribute is valid.
record.validationError
Detailed validation error information, or null
if the record and its aggregated attributes are valid.
An error object has tree structure mapping the invalid subtree of the aggregation tree.
// ValidationError object shape
{
error : /* record-level validation error msg as returned from record.validate() */,
// Attribute-level validation errors, one entry for each invalid attribute.
nested : {
// Contains nested ValidationError object for nested records and collections...
nestedRecord : /* ValidationError */
nestedCollection : /* ValidationError */
// ...and error msg for all other attributes.
otherAttr : /* attribute validation error as returned from .has.check() validator */
}
}
record.getValidationError( attr )
Return the validation error for the given attr
or collection's item.
record.eachValidationError( iteratee : ( error, key, recordOrCollection ) => void )
Recursively traverse aggregation tree errors. key
is null
for the record-level validation error (returned from validate()
).
recordOrCollection
is the reference to the current object.
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.
Collection is implicitly defined for every record with a constructor accessible as MyRecord.Collection
. In most cases, you don't need to declare it manually.
CollectionClass.Refs
Non-aggregating collection constructor.
static
model = RecordConstructor
Specify the record type inside of the collection's definition. This property is being set automatically for collection types referenced as MyRecord.Collection
.
@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
"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
"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.
collection.createSubset( records?, options? )
Create a non-aggregating serializable collection which is the subset of the given collection. Takes the same arguments as the collection's constructor.
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.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
.
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.
Change events
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.
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.
Serialization
All kinds of collections except Collection.Refs
are serializable.
collection.toJSON()
Produces the JSON for the given collection:
[ { record json }, ... ]
- array of objects for regular aggregating collection.[ id1, id2, ... ]
- array of record ids for the subset collection.
May be overridden in the particular record or collection class to customize the JSON representation.
@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" }] }
options
{ parse : true }
Parse raw JSON when passed as an option to the collection's constructor or update methods.
callback
collection.parse( json )
Invoked internally when { parse : true }
is passed. May be overridden to define custom JSON transformation. Should not be called explicitly.
Validation
Validation happens transparently on the first access to any part of the validation API. Validation results are cached. Only the changed parts of aggregation tree will be validated again.
callback
collection.validate()
Override in the collection subclass to add collection-level validation. Whatever is returned from validate()
is treated as an error message and triggers the validation error.
collection.isValid()
Returns true
if the collection is valid. Similar to !collection.validationError
.
collection.isValid( recordId )
Returns true
whenever the record from the collection is valid.
record.validationError
null
if the collection is valid, or the detailed information on validation errors.
- Aggregating collection is valid whenever
collection.validate()
returnsundefined
and all of its records are valid. - Non-aggregating collections are valid whenever
collection.validate()
returnsundefined
.
// ValidationError object shape
{
error : /* collection-level validation error msg as returned from collection.validate() */,
// Collection items validation errors
nested : {
// Contains nested ValidationError object for nested records...
/* record.cid */ : /* record.validationError */
}
}
collection.getValidationError( recordId )
Return the validation error for the given collection's item.
collection.eachValidationError( iteratee : ( error, key, recordOrCollection ) => void )
Recursively traverse aggregation tree errors. key
is null
for the record-level validation error (returned from validate()
).
recordOrCollection
is the reference to the current object.
Store
Store
is the special kind of record which serves as a root for ~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.
Definition
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.
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.
References to stores
recordOrCollection.getStore()
Return the closest store. Used internally to resolve symbolic ~references to the stores.
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.
attrDef
: Record.from( '~collectionFromStore' )
Serializable reference to the record from the collection taken from the closest store found with this.getStore()
call.
attrDef
: Collection.subsetOf( ~collectionFromStore
)
Serializable collection of references to the records from the collection taken from the closest store found with this.getStore()
call.
@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
}
}
Mixins
Type-R is based on mixins with configurable properties merge rules.
Definition
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'
...
@mixins( Events, plainObject, MyClass, ... )
class X {
...
}
decorator
@mixinRules({ propName : rule
, ... }) class ...
Specified class properties will be merged according to the given rule when both merging properties from the mixin or the base class. Rules can be extended and overridden in any subclass.
This allows for automatic chaining of the methods on inheritance.
Mixin rules
rule
propName : 'merge'
Assume the property to be an object. Merge objects from mixins.
rule
propName : { name1 : rule
, ... }
Assume the property to be an object. Recursively define merge rules for its properties.
rule
propName : '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
propName : 'sequence'
Assume the property to be the function. Call functions from mixins in sequence: f1.apply( this, arguments ); f2.apply( this, arguments );...
rule
propName : 'reverse'
Same as sequence, but functions are called in the reverse sequence.
rule
propName : 'mergeSequence'
Assume the property to be the function returning object. Merge objects returned by functions from mixins, executing them in sequence.
rule
propName : 'every'
Assume property to be the function returning boolean. Return true
if all functions from mixins return truthy values.
rule
propName : 'some'
Same as every
, but return true when at least one function from mixins returns true.


Event Location:
Canadian Western Bank
10303 Jasper Avenue NW
Edmonton, Alberta, Canada
Abstract:
There are many automated test frameworks available for different types of applications, but Extract, Transform, and Load (ETL) data movement applications seem to lack an efficient and easy to use automated test framework. We will review how to create automated tests with a data driven approach by showing how the tests self-generate based upon the data in the Information-Model schema in SQL Server and 4 slender custom tables that define the field data mapping between the source and target. The last half of the session will be a demonstration of the tests to illustrate how they execute. To do this we will quickly review the functionality of the tSQLt test framework and the stored procedure code we created that generates the tests. We will review the considerable functionality in tSQLt that makes ETL testing and database testing easy and efficient. We will execute hundreds of tests in the short time during the presentation.
Speaker(s): Terry Bunio,
Abstract:
Did you ever get woken up at night from a call from a user screaming about poor query performance, but didn’t really know how to begin attack the problem? In this session, we will explore the basics of execution plan analysis and how to single out issues that pertain to base table operations. In this presentation, we will identify what indexes are being used by the SQL Optimizer, the different type of index operations that are performed by SQL Server and which operations are generally considered the preferred method of data retrieval. We will discuss how to determine if the indexes used by the Optimizer are optimal, according to best practices, and the exceptions to those cases. Finally, we will cover the relationships between different types of index operations and joins within SQL Server, including how the SQL Optimizer determines which join type it will use, along with the pros and cons of each join operation
Speaker(s): Konstantin Melamud,
Abstract:
A lot have changed in terms of Visualization and Reporting on the Microsoft Data Platform in the last year. With the imminent release of SQL Server 2016 product and various Azure offerings innovating on a weekly basis, its hard to keep up with all of the options available to your organization. Let’s step through these options and look at the strengths of each to help you make a more informed choice. This session will be a balance of looking at the product and discussion of the offerings. We will look at features of SQL Server Reporting Services (SSRS) Paginate Reports, SSRS Mobile Reports, Power BI Desktop and the Power BI Services.
Speaker(s): Scott Stauffer,
Abstract:
Let me take you on a fast-paced journey through basic database internals, hardware and operating system setup, and how to configure SQL Server, to avoid hearing this most painful sentence for any DBA: "the database is slow". I talk quickly, and cover a lot of ground, but don't let that scare you.
Speaker(s): Randolph West,
Abstract:
Just getting started with SQL. Want to know more about how to write a select statement ? Ever wondered what a join statement does or how to get your results in a particular order or even how to group them. Then this session is for you. This session covers the basics to advanced of T-SQL query writing.
 There are several programming design patterns that work great in object orientated code that do not scale well inside of SQL Server. In this session we will focus some of these patterns and show you how to make them scale in the database engine. We will touch on Scalar-Value functions, Union, CTE, RBAR and more. After this session, you can go back to your office Monday morning and be the code tuning rock star!
Speaker(s): William Wolf,
Abstract:
Ever wondered with Azure Machine Learning was and what it can do for you? This introduction to AML that will get you on your way to great analytical models.
Speaker(s): Melody Zacharias,
Abstract:
Keynote on SQLServer 2016 new features.
Speaker(s): Mohit Gupta,
Abstract:
Backup types in SQL Server. Importance of not having a backup strategy and instead having a restore strategy. Backup and restore examples including: 1. Full DB backup and Restore 2. Filegroup Backup and restore 3. File Backup and Restore 4. page backup and restore 5. Special case scenarios example: restoring master 6. Tips on improving performance of backups Will try to take the audience from a beginner level to intermediate to advanced by giving examples. That way, they can get an idea on different options and ways of backing up and restoring and can use the scripts shown to practice later.
Speaker(s): mirza baig,
Abstract:
Analysis Services have become a major part of upper management daily decision making activities. However, with our DW sizes reaching multiple terabytes minimizing time required to process our data has become our top priority. In this session, we will cover the options we have as DBA’s to improve the performance of our DW processing time. We will look at features and general best practices available to us within SSAS as well as enterprise level features within the database engine itself and how aligning them to work side by side would yield us at least 200% improvement in our processing time.
Speaker(s): Konstantin Melamud,
Abstract:
Have you ever had to migrate a SQL Server Instance from one server to another? Are you a 24/7 shop with a strict maintenance window? Have you ever been asked to migrate 100's of databases in a matter of minutes to keep the outage as short as possible? In this session we will look at some techniques that can be used to migrate your SQL instances from one server to another using tools included with SQL Server. We will discuss how these strategies can be used to keep your down time requirements to a minimum and measured in minutes rather than hours.
Speaker(s): Ian Chamberland,
This is a list of speakers from the XML Guidebook records. The details and URLs were valid at the time of the event.
Contact: http://www.agilevoyageur.com
Terry started his career working with Adabas as a DBA., Over the next 20 years, Terry has worked with Oracle, Informix, Sybase, and Microsoft SQL Server as both a Data Modeler and DBA. Terry is a huge proponent of normalization and dimensional modeling. Terry has implemented Operational Data Stores, Data Warehouses, and ETL solutions for Investors Group, LPL Financial, Manitoba Blue Cross, and others. Terry has recently worked on integrating Agile practices into Database projects.
Konstantin has over 15 years of SQL Server experience and demonstrates his expertise and commitment to helping SQL Server DBAs succeed through his position as Director of Service Delivery at RDX. In his role, Konstantin is responsible for designing and implementing RDX’s service delivery strategies, including those for SQL Server. Since joining RDX in 2009, Konstantin has proven to be a strong leader and expert in SQL Server database administration, architecture, performance tuning, and business intelligence. He is also a very active member of the SQL Server community and frequently presents at SQL Saturdays and user groups throughout the year. In addition, Konstantin presented during 24 Hours of PASS and at the PASS Summits.
LinkedIn: https://www.linkedin.com/in/williamjwolf
Contact: http://www.sqlwarewolf.com
Bill is the Manager of SQL Server Performance Tuning for RDX. He works with over 400 customers on solutions within the SQL Server environment and specialized in code tuning and mentoring the team within RDX. He has become a SQL Saturday veteran speaking at nearly 40 SQL Saturdays in the last 3 years. He has also spoken at Summit and is an Idera Ace class of 2018. Bill is active in the Pittsburgh SQL Server User Group. He is also the chapter lead for the DevOps Virtual User Group. He is passionate about educating users on SQL Server and mentoring others.
Been a SQL Server Developer since Jan 2000. Been a DBA for 4-5 years now.
LinkedIn: https://www.linkedin.com/in/craigmillang
Data Migration developer working on CWB Project ARI, I have seen good data, bad data and have had to use quite a few abdominal queries to have data work corporate nicely between two systems. Favorite feature of SQL Server : CTEs because in order to enjoy recursion you have to enjoy recursion 4 Yrs. of SQL and C# experience
LinkedIn: https://www.linkedin.com/in/guptamohitk
Contact: http://www.sqlcan.com
Microsoft SQL Server PFE for 6+ years; focusing primary on PTO amp; HADR.
LinkedIn: http://www.linkedin.com/in/scottstauffer
Contact: http://scottstauffer.ca
Scott Stauffer (@SQLSocialite) is very PASSionate about data and enjoys meeting with like-minded data geeks. He started the Vancouver PASS Chapter in 2004. In 2008, Scott was recognized by Microsoft with a SQL Server MVP award. In 2009, Scott started a PASS BI Chapter in Vancouver, and was also at one time the PASS Regional Chapter Mentor for Canada. For over 10 years he has been an independent consultant working with clients to solve their data woes and extract insight from their business data.
Konstantin has over 15 years of SQL Server experience and demonstrates his expertise and commitment to helping SQL Server DBAs succeed through his position as Director of Service Delivery at RDX. In his role, Konstantin is responsible for designing and implementing RDX’s service delivery strategies, including those for SQL Server. Since joining RDX in 2009, Konstantin has proven to be a strong leader and expert in SQL Server database administration, architecture, performance tuning, and business intelligence. He is also a very active member of the SQL Server community and frequently presents at SQL Saturdays and user groups throughout the year. In addition, Konstantin presented during 24 Hours of PASS and at the PASS Summits.
LinkedIn: https://www.linkedin.com/in/williamjwolf
Contact: http://www.sqlwarewolf.com
Bill is the Manager of SQL Server Performance Tuning for RDX. He works with over 400 customers on solutions within the SQL Server environment and specialized in code tuning and mentoring the team within RDX. He has become a SQL Saturday veteran speaking at nearly 40 SQL Saturdays in the last 3 years. He has also spoken at Summit and is an Idera Ace class of 2018. Bill is active in the Pittsburgh SQL Server User Group. He is also the chapter lead for the DevOps Virtual User Group. He is passionate about educating users on SQL Server and mentoring others.
LinkedIn: http://ca.linkedin.com/in/melodyzacharias
Contact: http://sqlmelody.com
Melody is the Sr. Microsoft Solutions Manager at Pure and has been in love with data since 1991. She has been sharing her passion with the community in technical sessions and blogs since 2014. She has been a Microsoft MVP since 2016, including winning Rookie of the year for Canada that year. This last year, she was elected to the board of directors for PASS.org, the professional association for SQL Server and was awarded Regional Director by Microsoft. She has co-authored 3 books, including, SQL Server 2019 Administration inside out by Microsoft Press.
LinkedIn: http://ca.linkedin.com/in/ianachamberland
Ian has been working as a SQL Server Database Administrator since 2010. He focuses on new implementations of SQL Server environments and improving existing ones. He enjoys working with HA/DR technologies, ensuring performance requirements are met and automating tasks with PowerShell.
LinkedIn: https://ca.linkedin.com/in/rabryst
Contact: https://bornsql.ca/blog/
Randolph West (they / them), founder of Born SQL, is an independent IT consultant, speaker, Calgary PASS user group leader, Microsoft Data Platform MVP, and lead author of “SQL Server 2019 Administration Inside Out”. Randolph specialises in SQL Server performance tuning, disaster recovery, and migrations from really old versions, with an emphasis on implementing best practices. Randolph has presented at PASS Summit, SQLBits, SQL Saturdays, and user groups. You can also find Randolph acting and directing on screen or the stage, or annoying people on Twitter. Do not trust Randolph around chocolate.
Contact: http://www.agilevoyageur.com
Terry started his career working with Adabas as a DBA., Over the next 20 years, Terry has worked with Oracle, Informix, Sybase, and Microsoft SQL Server as both a Data Modeler and DBA. Terry is a huge proponent of normalization and dimensional modeling. Terry has implemented Operational Data Stores, Data Warehouses, and ETL solutions for Investors Group, LPL Financial, Manitoba Blue Cross, and others. Terry has recently worked on integrating Agile practices into Database projects.
The following is a list of sponsors that helped fund the event.


Who are we?
The Support Local Art Academy (SLAA) gets thousands of submissions every year for artists interesting in participating in the SLAA exhibits, and selects approximately 100 distinct pieces of contemporary art for display in their collective exhibit. In addition, 3 individuals are honored as Featured Artists - each being granted his or her own exhibit hall to display entire collections or themed pieces.
Each Featured Artist has an opportunity to speak at one of our meetups and share his or her vision, perspective, and techniques with attendees on a more personal level than at our large conference. It is truly an honor to be a SLAA Featured Artist and many past students artists who were featured at SLAA have gone on to brilliant careers in art.


Through amount of reference to successful Hp case constant
self-summarize, we finally concluded a 6-step set strategy to complete
Hp consistent with our project. We named them
:“Contact”，”illustrate”，”Conversation”，”Change”，”Advance”，”Nicole”.
Contact: In the process of our project in the researching term, we
accumulated our targets to somewhere we need to have a Hp, which we
call it "Valuable Library". We utilized this library to deal with many
problems in the way of our research. With the continuous accumulation
in the process of early stage and mid-term, we successfully encounter
lots of problems at various stages and never worry about possible
helpless situation. Of course the first step when we have matters is
to depend ourselves rather than just blindly seeking advice. The
library gives us more help in terms of our thoughts and vision.
Illustrate: A team should always clear their project progress,
problems and definitely their project. In terms of our project, we
focused our team's project plan and check our project in case any
likely problems. We had a list and it held up our problem that we have
not ruled out and though we always filled something new on the list,
the length of our list is shorter and shorter. Facing the problems
hard to solve, we will to shed light on the problem and check the
Library search may provide us with help of experts and the team.
Conversation: After we successfully handled ‘Illustrate’ and
‘Contact’, our human practices group would construct the
‘Conversation’ through every possible method. In the ‘Conversation’
section, we would explain our project and the problems we met, we
would record the communication process as well. We would take the next
step as soon as possible once we finished the ‘Conversation’ part.
Change:
We would reconsider and adjust our project according to our
conversation records, preventing our project come to a standstill.
Advance: Sometimes we were hesitated to decide among several
suggestions because we could not distinguish by the present situations
whether they were beneficial to our project, thus we had to give them
a try. Using different analyzing methods constantly during our
experiment, we could finally decide which suggestion was really
helpful and meaningful to improve our project.
Nicole:
As we reached to the ‘Nicole’ part, it indicates that our human
practices have successfully conducted. Although it might has some
drawbacks in this rapid project construction, this is the best case we
can come up with within the preparing period. Nowadays, it is not hard
to obtain the information we need. But it will be much easier and
handier to use the CICCAN system to get the information, including
human practices aim, conference records and project suggestions. By
using the CICCAN system, we can finally emend our project close to
perfect, avoid risks and successfully come to the ‘Nicole’ in time.
“If you have a dream, go get it.” That’s the response from our advisor
when we first pitch the idea of researching the mechanism of cancer.
So here we come with our accomplishments about CTCs and we sincerely
would like to receive more perspectives from other scholars in iGEM.
When it comes down to our drive of attending IGEM, our team have
focused on two aspects: learning and getting feedback. Throughout the
preparation for this conference, we have already learnt a lot about
experiments, communicating with Hp, how to minimize risks and how to
properly make choices. We truly realize that only learning in our own
circle is not enough to spawn a mature project. However, our team
never gives up easily and we still unswervingly believe that our
project will be able to contribute to the world eventually.
Throughout the entire project, we have encountered a lot of
challenges. Step by step, from the brainstorm of our theme, to the
decision of capturing CTCs, and to finally realize it in the lab, we
overcame all those barriers to finally reach our Wiki statement. We
continue to learn and break throughout the process. In the end, we
will bring our results to the conference, using this platform to
showcase our projects and accept feedback from our peers and other
scholars. Hopefully, we could carry the dream overseas to this amazing
continent and reply to ourselves with a satisfying result of our
dream.
Our project is not just for participating in this event. We certainly
have more expectations for this project to deliver more information
for future research. We firmly believe that the birth and use of any
kind of technology is accompanied by numerous experimental
verifications and sample analysis. Although our team may not be able
to think of a complete pathway to cure cancer in a short time, but our
project can provide a perfect experimental background for future
research. With our help, no team will encounter serious damage when
transferring, gene sequencing, doing experiments with CTC samples like
what Heipros met before. Therefore, we could ideally avoid the cost of
time and money on CTC-related research and, most importantly. we are
willing to help other iGEMer or research teams with full supports.
At the same time, we have brought new thinking to the community. Our
project technology focuses on the application of functional DNA, which
combines nanomaterials and biological fields to provide more options
in cross-disciplinary research. Plus, we plan to apply our aptamer
hydrogel initiation system to more fields, such as the precise capture
of rare cells. We hope to provide more choices for experimental
materials for more research in the future and assist researchers in
achieving their experimental goals of improving humanity.
We decided to do tumor-related work during the project research phase,
but as we all know, the tumor field is wide-ranging. We also need to
select specific projects that can meet our laboratory conditions and
iGEM cycle to conduct research. We started with immunization and the
use of Car-T technology, but, after discussing with Professor Xu Jie,
we realized that our laboratory may not provide sufficient
experimental conditions. Therefore, we conducted a re-investigation of
the project. After our conversations with Professor Hong Rui from
Liaoning Provincial Cancer Hospital, we realized the deficiency of
current CTC capturing technology to meet the demand of medical and
scientific researchers. Therefore, we used the sample provided by the
hospital to develop a possible pathway to approach the problem.
We decided to use DNA materials to design a low-cost cell capture
strategy by first constructing a cell cage through aptamers and DNA
hydrogels, and then using quantum dots and magnetic techniques for
separation. However, after consulting from researcher Cuihuan Qing
from Chinese Academy of Sciences Shenzhen Institute of Advanced
Technology, we learned about DNA prospects for the future development
of the hydrogel materials. Its low-cost and highly controllable
characteristics really attracts us for learning more about it.
Thereafter, we visited Shanghai Pu Los company, which is a precise
medical core business. In the process of communication with other
science and technology team, They suggested that we could focus on DNA
hydrogel, and that we should abandon the magnetic separation
technology to reduce costs.
Furthermore, based on out communication with LanJing lab, we decided
to make a CTC capture kit. This decision also provides a reference for
our subsequent experimental system. In the course of the experiment,
we use our CICCAN program, which can be seen later, to avoid risks and
have contacted Akkaya and Liu Dongsheng in advance. Group
communication, solving our problems in the specific experimental
process. In parts verification process, we with the help of Professor
Wang Xinkui, successfully verified parts viability.
Team DUT China A attaches great importance to the safety and security
of both scientists and the environment. Our host lab, Zi Laboratories,
follows General requirements for laboratory biosafety in China.All the
members involved in the experiment were trained strictly before thanks
to their professional course, our professional experimental teachers
helped us to conduct systematic experimental safety training right
before the lab work start.We strictly obey the rules of the laboratory
practices and guidelines. Three cell lines including HeLa (Epithelial
cell adhesion molecule negative, EpCAM-negative), MCF-7
(EpCAM-positive) and A431 (EpCAM-positive) used I our project are
commercialized and widely-used in research, without any pathogen.The
final aim of our project is to detect circulating tumor cells with
human blood. However, we will not conduct any test with blood but take
white cells to simulate real blood environment and carry out
experiments under such conditions.
After we reach a long-term cooperation in education, we realized that
we are really willing to devote ourselves for the future of human
evolution in life sciences. However, the world needs talents from all
other fields besides biology. Thus, in the process of education, we
never discredit students to think from other perspectives. Moreover,
we are paying more attention to how to connect our thoughts and
education while teaching biological theory. We have been doing so in
the past few years. From the academic accomplishments obtained under
academic research, to our determination of unswervingly persisting our
original intention, we all convey these to the students in different
forms, affecting their inner hearts in subtle ways. We try to provide
the students with a broad scope of thinking based on the life
sciences. Even if the students prefer other fields, they will still be
able to apply what they have learnt here to impact their future
selves. Overall, we hope that through such cooperation, we can
contribute more to students’ personal development.


Finished page in manuscript system
Manuscript-o-matic
Editing Manuscript: Effect of Manuscript Submission on Publication Count
Dataset in Dryad
Some Amazing Dataset
DOI: https://doi.org/10.7272/Q6QC01Q6
Size: 10MB
Reviewer Link: https://datadryad.org/stash/share/OI-tU-WmoT3I2KCOqX7Of624
Edit this dataset
Return to Embedded Submission Demo
Prev Page
Next Page
<EMAIL_ADDRESS>Last modified: Wed Oct 21 11:32:39 EDT 2020


{{ index (findRE "(.|\n)*?" $element 1) 0 | plainify }}
{{/*
Designer
*/}}
{{/*
You can write here details about one of your team members. You can give more details about what they do. Feel free to add some
links for people to be able to follow them outside the site.


Shashank likes strings in which consecutive characters are different. For example, he likes ABABA, while he doesn't like ABAA. Given a string containing characters $A$ and $B$ only, he wants to change it into a string he likes. To do this, he is allowed to delete the characters in the string.
Your task is to find the minimum number of required deletions.
Input Format
The first line contains an integer $T$, i.e. the number of test cases.
The next $T$ lines contain a string each.
Output Format
For each test case, print the minimum number of deletions required.
Constraints
$ 1 \le T \le 10$
$ 1 \le$ length of string $\le 10^5$
Sample Input
5
AAAA
BBBBB
ABABABAB
BABABA
AAABBB
Sample Output
3
4
0
0
4
Explanation
AAAA $\Longrightarrow$ A, 3 deletions
BBBBB $\Longrightarrow$ B, 4 deletions
ABABABAB $\Longrightarrow$ ABABABAB, 0 deletions
BABABA $\Longrightarrow$ BABABA, 0 deletions
AAABBB $\Longrightarrow$ AB, 4 deletions because to convert it to AB we need to delete 2 A's and 2 B's


Map of Current Locations
The current positions of sites B1 and B2 are displayed on the map below. The ground overlay is the latest cloud-free Landsat 5 image of the Bering Glacier, dated May 25, 2011.
Zoom in on the sites to see their migration tracks as a red line (you can zoom in to an ablation site by
clicking its marker on the map). To see the average daily position of the sites since they were installed (which may help you to see how fast the sites are moving) toggle the option below.
Average Daily Position: Show | Hide
www.BeringGlacier.orgThis website and its content are not administered or endorsed by either the U.S. Bureau of Land Management or the U.S. Geological Survey.


Shrek * * * 1/2 (out of * * * * )
Directed by Andrew Adamson, Vicky Jenson. Voices: Mike Myers, Eddie Murphy, Cameron Diaz, John Lithgow. 2001 - 89 minutes Rated PG (for mild language). Reviewed by Dustin Putman, May 19, 2001.
A world in which all fairy tale creatures co-habitate with humans, Shrek (Mike Myers) is a lonely, green ogre whom everyone fears. Living in a quaint cottage by a swamp, he is distressed to discover that the fairy tale creatures, including the Big Bad Wolf, the Three Little Pigs, The Three Bears, the Three Blind Mice, and Pinocchio, have all been rounded up in his backyard, under the direct orders of Lord Farquaad (John Lithgow). In an attempt to get his land back, Shrek journeys to the theme park-like castle in the town of Duloc to talk to Lord Farquaad, with blabbermouth Donkey (Eddie Murphy) in tow. Since Lord Farquaad can't become King until he marries a princess, he agrees to give Shrek his land back, in exchange for rescuing the feisty Princess Fiona (Cameron Diaz), who is locked away in another castle, guarded by a fire-breathing dragon, and bringing her back to him.
The second computer-generated animated film from Dreamworks (with the first being the disappointing 1998 movie, "Antz"), "Shrek" is a dazzling display of creative artistry and sheer imagination. With animated characters never looking so flesh-and-blood before (at least until "Final Fantasy" is released later this summer), the movie is nonstop candy for the eyes, with a palette of bright colors jumping off the screen, and a round-up of central characters who are not only likable, but also memorable and enchanting.
With a PG rating, for some admittedly racy material for a family film, "Shrek" is an ideal viewing experience for both children and grown-ups alike. There is humor throughout that kids will be able to thoroughly enjoy, but also a constant slew of jokes that will have the adults rolling (if my theater of mostly over-15's is any indication). When Lord Farquaad, for example, is deciding on a princess to marry, he is presented by the Mirror from "Snow White and the Seven Dwarfs" with three contestants, a 'la "The Dating Game"--Cinderella, Snow White, and Fiona. About Snow White, the mirror says in its best TV-personality voice, "...and although she may live with seven men, she's not easy!" Fiona, meanwhile, "enjoys sipping pina colatas and taking long walks in the rain." And when Fiona is the lucky winner of the contest, the song, "If You Like Pina Colatas," bursts onto the soundtrack. "Shrek" is the funniest animated film since Trey Parker's outrageous, R-rated 1999 feature, "South Park: Bigger, Longer, and Uncut."
Directors Andrew Adamson and Vicky Jenson, and screenwriters Ted Elliott and Terry Rossio, based on the children's book by William Steig, delight in showing off their fast, zippy, satirical dialogue, and have a lot of fun playing with fairy tale conventions. Nearly every fairy tale creature you can think of makes some sort of cameo here, as does the actual Disneyland theme park, cleverly disguised as Lord Farquaad's living quarter. Another rib-tickling moment comes when the Gingerbread Man is being tortured by Lord Farquaad, in an attempt to get information out of him.
The voice work from all four principals is wonderful. Mike Myers develops Shrek into a lovable ogre who is tired of being feared, and just a little bit lonelier than he outwardly appears.  Cameron Diaz cleverly gives Princess Fiona a bit of punch by portraying her much like her butt-kicking, live-action character in 2000's "Charlie's Angels." And John Lithgow also amuses as the very short Lord Farquaad. Remarking about the monstrous size of his castle, Donkey proclaims, "A castle that big must mean he's compensating for something."
Aside from a ten-minute section at the start of the third act, in which a misunderstanding between two characters brings added conflict, and briefly turns the movie into a tedious one, "Shrek" is a fabulous, unadulterated entertainment, and comes back just in time for a winning finale. Word is that there are already plans for a "Shrek 2," and that's fine with me. There is a great deal of unchartered territory that this first picture doesn't even begin to scrape, and undoubtedly more adventures for Shrek to go on. Whatever the case is with the rumored sequel, one thing is for sure: "Shrek" is a magical film not to be missed on the big-screen, by anyone. A new animated classic has been born.
- Copyright 2001 by Dustin Putman http://www.all-reviews.com/reviewers/DustinPutman.htm http://us.imdb.com/reviewsby?Dustin+Putman http://www.atnzone.com
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


It’s an exciting time for Mozilla and the Web. Another two billion people will join the Internet community in the coming years.
Mitchell Baker
{{ _('Welcome to the Mozilla community directory') }}
This site lists all vouched individuals that participated in the Mozilla Project while this site was active.
{{ number }}
{% endif %}{{ fact.public_text|markdown }}
{% else %}3000+
{% trans %} Passionate Mozillians you can meet for work, play or a cup of coffee. {% endtrans %}
{% endif %}


The model is usually characterized by performing calculations, and makes recommendations based on that information. The model may also summarize particular events for the end user and provide direction regarding possible actions or alternatives.
Business decisions are increasingly made based on a financial model built in around the system. HMIT incorporated some simple best practice principles in the form of models that can improve the flexibility and reliability of Business value decisions.
Key Statistics
PS1 Analysis
Table
| Variable | Signal | Statistics | ????? | Jan | Feb | Mar | % |
| 5 | All | Mean | 0.5 | 0.5 | 0.3 | 0.4 | -18% |
| 4 | All | Standard Deviation | 0.2 | 0.23 | 0.19 | 0.27 | 30% |
Performance Trend - 1
Performance Trend - 2
Alerts Thresholds
| Metric | Category | Alert 1 | Choice 1 | Alert 2 | Choice 2 | Add+ |
| Score PSI | Stability | Critical | Minor | |||
| Score Mean Change | Stability | Critical | Minor | |||
| KS Drop | Performance | Critical | Minor | |||
| Gini Drop | Performance | Critical | Minor | |||
| ALROC Drop | Performance | Critical | Minor | |||
| (+ Add/Remove Metric) |
Overview Preferences
| Alerts count |
|
| Top Alerts displayed |
Stability & Performance Preferences
| # of periods in time series | |
| Show training Value (INS) |
|
| Main Statistics displayed (Stability) |
|


At Pizza Hut, we know you want options. Pepperoni? Boring. Cheese? No way! Sausage? C'mon, let's get creative.Checkout Our freshly made Delicious Pizza .
We have every pizza ingredient imaginable in our kitchen because we know you take choice seriously. Take a look through our menu of hundreds of pizza options and we know that you'll find something you love.
All your Favorite's in one place, Take your pick
Delivery will cost $2
Pizza HUT KENYA, Nairobi00100254-716-175-419


--- title: 'Floe - flexible learning for open education' ---
FLOE provides the resources to personalize how we each learn and to address barriers to learning. Learners learn differently, and today’s society needs diverse, self-aware, life-long learners.
FLOE supports learners, educators and curriculum producers in achieving one-size-fits-one learning design for the full diversity of learners, leveraging the variants made possible by Open Education Resources (OER).
Floe is led by the Inclusive Design Research Centre and applies Inclusive Design to open learning.Being able to transform, augment, and select alternative educational resources to fit individual needs is essential for an inclusive learning experience
The HTML5 Video Player offers full keyboard access, captions, transcripts, descriptions and responds to personal user preferences. Learners can request missing captions, descriptions and transcripts, or create them .
Students with disabilities often depend on digital technology and adapted instruction to participate in class, communicate with family, and share with their friends. Our goal is to empower all learners to be creators of their digital worlds, to express themselves using code and art, and to apply these skills to other areas of learning and daily life. Not just learning to code, but coding to learn and create.
The Inclusive Learning Design Handbook is an expanding source of guidance, tools and tips for producing inclusively designed learning resources, including guidelines for creating inclusive EPUB 3 resources, accessible standardized tests, and inclusive web games and simulations. Floe has created The Inclusive Design Guide - an engaging and evolving resource for learning about and applying Inclusive Design principles, practices, and tools to the design process. The Metadata Editor makes it simple to provide useful labels regarding the learner preferences the resource can meet, and supports authors in adding accessibility features.
The First Discovery Tool helps newcomers learn about what kinds of personalizations are possible, and what might work for them.
The Preference Exploration Tool offers a set of starter preferences to try - from reading content aloud so it's easier to follow along, to enhancing keyboard interactions so it's easier to use.
The My Lifelong Learning Lab helps learners keep track of their progress, and personalize their learning goals and learning experience through self-reflection.
The Preference Management Tools support portable preferences - you can apply preferences across devices, resources and learning applications using the Global Public Inclusive Infrastructure.


Use the options below to import an existing wallet or create a new one
When you create a new wallet, a new Ceviz seed will be generated which can be used to create brand new accounts on the Ceviz network.
Make sure you back up your seed, write it down, and keep it incredibly safe! All funds inside can be lost without it!
If you already have a Ceviz wallet, you can import it below. When you import a wallet, it replaces the previous one in the GUI but none of your existing accounts are affected since the wallet can be restored simply by importing it again. The seed is not stored anywhere besides in your local client so please make sure you have it stored elsewhere. Use the drop down below to select which type of import you want to use.
If you have saved your wallet to a file using CevizVault before, use the button below to import it.
Enter your mnemonic generated from a Ledger device or certain multi-currency wallets. If it was generated in CevizVault, use "Ceviz Mnemonic" import type instead.
Make sure you have the Ceviz app installed, then connect your ledger device via USB. Ledger/CevizVault User Guide
Below is the seed and mnemonic backup. You can use whichever you prefer in any other compatible wallet.
{{ (l * 4) + i + 1 }} {{ word }}
Copy mnemonic to clipboard
Your password is used to encrypt the sensitive parts of your wallet when it is locked, which disables sending, receiving, creating accounts, and other operations.
For the safety of your funds, use a secure (green) password.
Your wallet has been created and you are ready to send and receive Ceviz!
It will lock after a certain amount of inactivity which can be changed in the app settings.

ProTip: Press the Ledger status bar on the left side to reload your Ledger when connecting or disconnecting it.