Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
A javascript mash-up framework
JavaScript
branch: master

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.
tests
vendor
README.md
index.html
models.js
trademark.md
triforce.js
views.js

README.md

TriforceJS

TriforceJS is client-side JavaScript framework suitable for mash-ups. What makes Triforce different from other frameworks is that it follows the eventual consistency model and across multiple data sources.

It will consist of the following libraries

  • Futures - Asynchronous Toolbox (Futures, Promises, Joins, Subscriptions, Synchronizations, etc)
  • AHR - An Abstracted HTTP Client for node and the browser
  • Cimplate - CSS-based HTML scaffolding, templating, and data-binding
  • JSON-API - JSON document-driven design for REST APIs and clients

Scenario: Say you want to use Facebook, Flickr, and Blogger together. You may be getting a list of friends from facebook, cross-referencing them with e-mail addresses stored on your server, and then pulling content from Flickr and Blogger.

  • You're not using just one REST interface.
  • You're not sure when data will be available (and it will take a long time - as Facebook sometimes does)
  • Triforce let's you keep local caches of the results using some variant of localStorage.
  • You define mixin-models with custom cache expiration times.
  • When the user next uses your application the available data will be used and updated in the background only if the cache is stale.

Vision

This examples below are not representative of what TriforceJS currently does, but is representative of the goal.

Simplify the programming interface for simple and complex javascript mash-ups in a rather plug-n-play fashion.

Imagine that instead of managing a nasty web of callbacks that such things were possible:

// Define methods for getSome, getIndex, updateSome, deleteSome, etc
M = Triforce.Model('name_of_model', {
  // Each time the cache is hit the timestamp is checked
  // if this much time has passed, show the current data,
  // and also update in the background so that the cache will
  // have the current copy
  'stale_after' : 60*60*1000,
  // If the cache hasn't been updated in this long, don't show the user data.
  // Instead wait until fresh data can be shown
  'useless_after' : 24*60*60*1000, // How long before
  // Use WebStorage, SQLStorage, or some other persistent storage through jStore
  'persist' : true, // be careful with this, space is limited to about 512kb
  // Use fetchSome instead whereever possible
  // 'fetchOne' : function (id) {}, 
  // A promiseable which returns an array of data
  'fetchSome' : function (ids) {},
  // Provide an index and fetchAll will be created using fetchSome
  'fetchIndex' : function () {},
  // Get all known elements, fetchIndex will be generated for you
  //'fetchAll' : function () {},
  // Provide a way to fetch elemnts.
  // This will be wrapped automatically to search the cache first,
  // Hence your params should contain a key which to search
  'fetchSearch' : function (keywords, params) {}
});

// Let's say that I wanted to prefetch something but not actually do anything with it yet:
M.one(id);
M.some(ids);
M.index();
M.all();
M.search('keyword', { key : 'keyname' });

// I'll create a view that I'm associating with this model 
// (meaning that untransformed data will be passed to it)
M.createView('name_of_view', 'css3_selector', pure_directive)
  .before(func)
  .during(func)
  .after(func);

// I want the end-user to be able to interact with my page
M.delegate('listener_selector', 'matching_selector', 'event')
  .when(function () {
    // Ideally I'd have something like the reverse of PURE to create data from the dom
    date = this.scrape();
    this.updateOne(data[0]);
    this.updateSome(data);
    // And a PURE-like (directive-based) validator should be part of the model,
    // but for now the user could pass in custom functions to validate 
    // one model element as a whole
  });
// I'm not sure that this is the best way to handle events...
// The above doesn't quite feel right yet...

// Some simple transformations which don't add outside models
M.sortBy('name_of_sorter', func);

// Now I'll render some of those prefetched items
M.one(id).render('name_of_other_view');
M.some(ids).sortBy('name_of_sorter').render('name_of_view');


// Some other possibilities
Amz.search('Spider Man').render('spotlight_items');
Amz.search(['Spider Man', 'Batman', 'Superman'])
  .each(function () { this.select(10); })
  .randomize()
  .select(10)
  .render('spotlight_items');
amz_promise = Amazon.get('ASINXYZ');
amz_promise.when(my_func).render('preview_item');
fb_promise = FB.get('FBUID');
User.get(fb_promise).update('favorite_item', amz_promise);

Goals

  • Provide .get, .one, .some, .all, .search, .render, .when, .fail
  • Document non-trivial examples
    • Multiple-resource models (IE namespacing an Amz gift list to a FB user)s
    • Single-resource-only (IE auth) models
  • Provide .subscribe / .follow, .unsubscribe / .unfollow
  • Clean-up code for production use
Something went wrong with that request. Please try again.