Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Ambitious Data Flows with Ember.js and Orbit.js

Ambitious Data Flows with Ember.js and Orbit.js

Introduction to Orbit.js and Ember-Orbit, including some development patterns for working with client-side data. Presented at EmberFest 2014 in Barcelona.

Dan Gebhardt

August 28, 2014
Tweet

More Decks by Dan Gebhardt

Other Decks in Programming

Transcript

  1. 1 2 3 4 5 6 7 ??? P P

    = Promise SYNCHRONOUS EVENT HANDLING
  2. PROMISE-AWARE EVENTS 1 2 3 4 5 6 7 8

    P P P P P = Promise Async Blocking
  3. PROMISE-AWARE EVENTS 1 2 3 ??? 4 5 6 7

    P = Promise P P Async Non-Blocking
  4. REQUEST FLOW << assistFind >>! _find()! << rescueFind >>! <<

    didFind /didNotFind >> source.find(“planet”, “1”)
  5. REQUEST FLOW << assistFind >>! _find()! << rescueFind >>! <<

    didFind /didNotFind >> source.find(“planet”, “1”)
  6. REQUEST FLOW << assistFind >>! _find()! << rescueFind >>! <<

    didFind /didNotFind >> source.find(“planet”, “1”)
  7. Orbit.Document Complete implementation of JSON Patch (RFC 6902): { }

    add remove replace move copy test ———— transform retrieve
  8. TRANSFORMATIONS JSON Patch transformations: {"op":"remove","path":["planet","1389295329110.3"]}! {"op":"add","path":["planet","1389295895882.1"],"value": {"name":"Mercury","id":"1389295895882.1"}}! {"op":"remove","path":["planet","1389295895882.1"]}! {"op":"add","path":["planet","1389296188090.2"],"value": {"name":"Mercury","id":"1389296188090.2"}}!

    {"op":"add","path":["planet","1389296196274.3"],"value": {"name":"Venus","id":"1389296196274.3"}}! {"op":"add","path":["planet","1389296197897.4"],"value": {"name":"Earth","id":"1389296197897.4"}}! {"op":"add","path":["planet","1389296199041.5"],"value": {"name":"Mars","id":"1389296199041.5"}}! !
  9. CONNECTORS • TransformConnector for connecting a source and target. •

    Two connectors are needed for bi-directional syncs. • Connectors can be blocking or not • RequestConnector for coordinating requests to data
  10. { } MEMORY JSON API LOCAL STORAGE + MORE +

    ORBIT COMMON LIB 
 SOURCES
  11. Provides a loose wrapper around an OC.Source: App.LocalStorageSource = EO.Source.extend({!

    orbitSourceClass: OC.LocalStorageSource,! orbitSourceOptions: {! namespace: “myApp” // n.s. for localStorage! }! }); EO.Source ember-orbit
  12. • Extends EO.Source • Maintains an identity map of model

    instances. • Familiar interfaces to access and request data. EO.Store ember-orbit
  13. EO.Store all filter retrieve ! find add remove patch findLink

    addLink removeLink { } { } Synchronous Asynchronous ember-orbit
  14. • Defines schema with attributes and relationships • Backed directly

    by data in the source • Familiar interfaces to access and request data EO.Model ember-orbit
  15. Star = EO.Model.extend({! name: attr('string'),! planets: hasMany('planet', {inverse: 'sun'})! });!

    ! Planet = EO.Model.extend({! name: attr('string'),! classification: attr('string'),! sun: hasOne('star', {inverse: 'planets'})! }); EO.Model ember-orbit
  16. Star = EO.Model.extend({! name: attr('string'),! planets: hasMany('planet', {inverse: 'sun'})! });!

    ! Planet = EO.Model.extend({! name: attr('string'),! classification: attr('string'),! sun: hasOne('star', {inverse: 'planets'})! }); EO.Model ember-orbit
  17. store.add(“planet”, {name: “Jupiter”}).then(! function(jupiter) {! jupiter.get(“moons”).pushObject(io);! jupiter.set(“classification”, “gas giant”);! }!

    );! ! store.then(function() { // all requests resolve! console.log(io.get(“planet.name”)); // Jupiter! });! EO.Model ember-orbit
  18. ember-orbit // Start development with just a local store! var

    LocalStore = EO.Store.extend({! orbitSourceClass: OC.LocalStorageSource,! orbitSourceOptions: { namespace: “myApp” }! });! ! App.initializer({! name: 'injectStore',! initialize: function(container, application) {! Orbit.Promise = Ember.RSVP.Promise;! application.register('schema:main', EO.Schema);! application.register('store:main', LocalStore);! application.inject('controller', 'store', 'store:main');! application.inject('route', 'store', 'store:main');! }! });
  19. ember-orbit // Prefer IndexedDB, but default to LocalStorage! function localStoreClass()

    {! if (window.indexedDB) {! ! return IndexedDBSource; // Coming soon!! } else {! return LocalStorageSource;! }! }! ! App.initializer({! name: 'injectStore',! initialize: function(container, application) {! application.register('store:main', localStoreClass());! }! });
  20. ember-orbit // Create transform connectors between sources! // Typically performed

    in the initializer! function connectSources(container) {! var main = container.lookup('store:main').orbitSource;! var api = container.lookup('source:api').orbitSource;! ! new Orbit.TransformConnector(main, api);! new Orbit.TransformConnector(api, main);! }!
  21. ember-orbit export default Ember.Controller.extend({! // Configure editing context (WIP!!!!!)! beginEditing:

    function(model) {! var _this = this;! var store = this.get('store');! var storeContext = store.createContext();! ! this.set('model', model);! ! storeContext.find('contact', 
 this.get('model.id')).then(function(contact) {! _this.set('editableModel', contact);! });! }! ! // Continued . . . WIP!
  22. ember-orbit // . . . Continued! ! actions: {! save:

    function() {! var _this = this;! this.get(‘editableModel.store')! .commitTransaction().then(function() {
 _this.transitionToRoute('contact.index');! })! },! ! cancel: function() {! this.transitionToRoute('contact.index');! }! }! }); WIP!
  23. ember-orbit ! ! // Watch for transforms and track them

    in an array! var transforms = this.get('transformations');! ! store.orbitSource.on('didTransform', ! function(operation, inverse) {! if (!_this.get('undoing')) {! transforms.pushObject(Transformation.create({! operation: operation,! inverse: inverse! }));! }! }! );! ! // Continued . . .! WIP!
  24. ember-orbit ! // . . . Continued! ! undo: function()

    {! var transformation = this.get('transformations').popObject();! var inverseOps = transformation.inverse;! var orbitSource = this.get('store.orbitSource');! var _this = this;! ! this.set('undoing', true);! orbitSource.transform(inverse).then(function() {! _this.set('undoing', false);! });! }! WIP!
  25. STATUS • Initial release January 9, 2014 • Current: 0.5.1

    ember-orbit • Initial release June 17, 2014 • Current: 0.3.0
  26. RECENT UPDATES • Full class system • Primary + secondary

    keys • Custom keys per model • Default settings for models • __meta key for source-specific data • Full JSON API support • Serializers (including JSON API)
  27. TODOS ember-orbit • Fully encapsulate Orbit’s API • Improve ergonomics

    • Add-on for ember-cli • More sources • More synchronization strategies • Coalescing / managing transforms
  28. COMING SOON orbitjs.com API docs and guides for Orbit and

    Ember-Orbit Follow progress: Twitter: @orbitjs
 IRC: #orbitjs
 Github: https://github.com/orbitjs
  29. ALSO COMING SOON Example app using JSON API Backed by

    Rails app built with JSONAPI::Resources.
 
 http://github.com/cerebris/jsonapi-resources