Giter VIP home page Giter VIP logo

rekord's Introduction

Rekord

Build Status devDependency Status Dependency Status License Alpha

Rekord is an ORM - a way to define properties and relationships - that interacts with local storage, a RESTful service, and a real-time service. Rekord does this in a generic way so you can use any libraries you wish or your own implementation - making it very easy to integrate into your existing projects. Rekord's design allows for offline capable and real-time applications if desired - offering complete control over how and where your data is stored. Rekord is the most powerful client-side Model/Active Record/ORM you'll find guaranteed - or your money back!

Download Stats

Features

  • Relationships hasOne, belongsTo, hasMany, hasManyThrough, hasRemote, hasList, & hasReference
  • Polymorphic relationships for hasOne, belongsTo & hasMany
  • Validation (59 rules, 6 expressions, 14 transforms, and custom functions) through rekord-validation
  • Migrations through rekord-migrations
  • "Sessions" through rekord-session
  • Batch REST execution for any/all types and operations
  • Inheritance (with extend option)
  • Horizontal scaling with sharding
  • Supports composite keys
  • Purging algorithms and "Contexts" to control memory/local storage usage
  • Specify default values
  • Handle collisions with a "revision" field
  • Handle propagating primary key changes returned by the server
  • Automatically refresh when application becomes online
  • Cache no data, all data, or only pending changes
  • Send only changed values to REST/real-time APIs or entire object
  • Convert values between client & server data types
  • Easily order by field, combination of fields, custom function, or expression
  • Use "Projections" to define subsets of data for efficient use
  • Control what information from relationships (if any) is stored locally or sent to the REST api
  • Add dynamic fields to model objects (setting & getting)
  • Data returned from REST calls or real-time events is intelligibly merged to avoid overwriting local unsaved changes
  • Add updated_at and created_at timestamps and their automatic behavior with a single option
  • Configurable date/timestamp transformations
  • Add custom methods to the model objects
  • Asynchronous methods return Promises which can be chained together
  • Load bootstrapped data with model.boot( model or array of models )
  • Execute searches (fields are sent to REST API and an array of models is expected) with model.search( query, options, ... )
  • Execute paginated searches
  • Add global event listeners to the "database" or all model instances
  • Stores data locally through Rekord.store interface (ex: storkjs)
  • Stores data remotely through Rekord.rest interface (ex: angular, jquery, ajax, pouchdb, firebase, knexjs)
  • Real-time changes through Rekord.live interface (ex: pubsub, pouchdb, firebase)
  • Create a live filtered view of any collection
  • Create a live paginated view of any collection
  • All collections have the following notable operations: sort, page, filtered, where, subtract, intersect, complement, clear, removeWhere, min, max, first, last, sum, avg, count, pluck, reduce, random, chunk, reverse, & group
  • Model collections have the following notable operations: removeWhere, update, updateWhere, & saveWhere

FAQ (client-side usage)

  1. Does Rekord directly interact with a database?
    No, of course not. It interacts with a REST API.

  2. Why do I need to use Rekord?
    Working with relational data in javascript can be painful. Rekord eases that pain by allowing you to use plain looking objects that can have any type of relationship with other objects. Rekord takes into consideration things like foreign keys - where you need object A successfully remotely saved before you can save object B. These types of constraints are ugly and bothersome to handle yourself and easily result in bugs. If you're familiar with server-side ORMs, then Rekord should be really easy to pick up. You'll find all the same features and even more!

  3. How are ID collisions avoided?
    The key for a model can be given when creating a model - otherwise the key will be given a UUID. This is necessary to be offline capable, models need keys so related models can reference it. If the keyChanges option is used the server can return a different key (like an auto-incrementing value) and the key changes will be propagated to all references to that model (foreign keys).

  4. What do you mean by capable?
    Caching data/changes locally and real-time behavior is optional - if you don't want either feature then you don't need to include an implementation.

  5. Rekord can handle horizontal scaling via sharding?
    Yes! You can say models of type X can exist on REST endpoints A, B, & C. You can provide a function which takes a model and returns the set of REST endpoints that need to be sent saves/removes. When you query on a sharded type it can contact all REST endpoints and combine the results.

  6. Why do some functions in the API start with $?
    The Rekord.Model and Rekord.Search classes can have custom properties therefore to avoid collisions the functions and private variables start with $. If your design includes properties like status, operation, db, relations, etc it won't interfere with Rekord.

Installation

The easiest way to install rekord is through bower via bower install rekord.

  • rekord.js is 387KB (68KB gzipped)
  • rekord.min.js is 115KB (29KB gzipped)

Examples

Examples exist in a separate project: https://github.com/Rekord/rekord-examples

Bindings

Bindings are used to implement core pieces of functionality in rekord - these interfaces allows any library to work with rekord.

  • Angular - implements Rekord.rest and adds Rekord.Sync
  • React - adds Rekord.Sync
  • StorkJS - implements Rekord.store
  • PubSub - implements Rekord.live
  • Firebase - implements Rekord.store, Rekord.rest, & Rekord.live
  • PouchDB - implements Rekord.store, Rekord.rest, & Rekord.live
  • jQuery - implements Rekord.rest
  • Ajax - implements Rekord.rest, dependency free
  • Knex.JS - implements Rekord.rest on the server-side
  • Debugging - implements Rekord.debug

Add-Ons

Add-Ons add new functionality to Rekord.

Rekord's Life Cycle:

Rekord Life Cycle

Documentation

The main documentation is located here. Additional documentation can be found here:

rekord's People

Contributors

clickermonkey avatar lguzzon avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rekord's Issues

Add cascade options

Possible options

  • [0] None
  • [1] Local
  • [2] Rest
  • [4] Live
  • [6] Remote (rest & live)
  • [7] All (rest, local & live)

That way you can do things like Neuro.Cascade.Local | Neuro.Cascade.Live if you wanted (uses bitwise operations to determine what to do).

NeuroOperations will need to accept a new parameter called cascade.

Refactor Neuro.rest

Instead of Neuro.rest(database)(options, success, failure) it will have the format Neuro.rest(database)(method, model, data, success, failure)

Add polymorphic relationships

A model can be related to multiple models based on discriminating id & type.

nameOfMorph: {
  fields: 'parent_id', // field or fields that point to related object
  discriminator: 'parent_type', // field that determines the 
  mapping: { // a mapping from discriminator to the class
    'discriminator_value': DiscriminatedClass
  }
}

A model can reference a discriminated model

nameOfRelatedMorph: {
   model: ClassWithDiscriminator,
   morph: 'nameOfMorphInClassAbove',
}

Add NeuroDatabase.ready

Add ready function which triggers a given function when the database has loaded. If the database is already loaded, the function should be invoked immediately. If the given function returns === false then we need to remove the listener.

This will be useful for relationships - they can't fetch related models until they know the related database has been loaded.

Correct usage of $key by using $hash.

The results of $key is not necessarily the key of the model - since the model can have a composite key. It would be better to call this hash/$hash throughout the code to be clearer.

Add Testing Framework

  • Setup test folder.
  • Use QUnit.
  • Create store, rest, & live implementations that can easily be modified outside of neurosync to simulate real world services.
  • More Issues to come to cover individual areas of the code base.

Decouple StorkJS as storage engine

Add a function called Neuro.store like Neuro.rest which is responsible for returning a storage implementation.

For example:

Neuro.store = function(database) // NeuroDatabase
{
  return {
    put: function(key, record, success, failure) { },
    remove: function(key, success, failure) { },
    all: function(success, failure) { }
  };
};

The use of promises in NeuroDatabase.js will have to be refactored as well to be decoupled from StorkJS.

Add NeuroDatabase.Defaults

Should be used to populate default values on every NeuroDatabase instance created. This will remove the odd db.cache !== false logic.

Remove Least-Recently-Used models from local storage when it's full and new models failed to save.

In the event that the device's local storage implementation can't fit all of the models for an application in the cache - add an $age to all models (time in millis or a counter will suffice) and a method to $touch() a model which updates it's age to avoid removal.

This will increase user experience by keeping the most frequently used models in local storage in the event of offline usage.

Not sure how we will communicate to the developer/user that a database is missing models because they weren't recently used enough.

Composite Keys

The key option passed to Neuro should allow arrays. This also requires that a new option be added called keySeparator which is used to take multiple columns and combine them together into a single key string.

Additional NeuroModel & NeuroDatabase events

. Event description (event data)

NeuroModel events

  1. Remote data has been received and a model has partially been updated (fields updated, fields with local changes)
  2. Remote data has been received and a model has fully been updated (fields updated)
  3. Remote data has been received and a model has been updated (fields sent) - always triggered after 1. or 2.
  4. Remote removal

NeuroDatabase events

  1. Model added (model)
  2. Model removed (model)
  3. Model updated (model)

Add hasManyThrough relationship

Possible parameters:

Example User model has many Groups through UserGroups

hasManyThrough: {
  groups: {
    local: 'user_id',
    model: Group,
    foreign: 'group_id'
    through: UserGroup,
    cascadeRemove: true,
    cascadeSave: true,
    comparator: ...,
    property: ...,
    save: ...,
    store: ...
  }
}
  • If through has a model added AND it's related to the model AND its not a related model that is already related, add it.
  • If through has a model removed AND it's related to the model AND its already related, remove it.

Add relationships (1-N, 1-1, N-1, M-N)

Add options to pass to Neuro function called belongsTo, hasOne, hasMany, hasManyThrough

  • Every relationship has a property on the model to which it's stored on.
  • Options should be available to either store the object or it's ID in the property.
  • belongsTo and hasOne refers to a single item where hasMany and hasManyThrough is an array. - Options should be available to send or not send the property the rest/live services.
  • Options should be available to load models from rest/live services.
  • belongsTo has a key on the current model that points to another model. The other model may have a hasMany relationship back.
  • hasOne has a key on the current model that points to another model. The other model most likely doesn't have a hasMany relationship back because it typically doesn't know who it belongs to. When a relationship is removed from the model the related model is permanently deleted. A cascade = false option can be specified to override this functionality.
  • hasMany has a key on the related model - so when a relationship is removed from the model the related model is permanently deleted. A cascade = false option can be specified to override this functionality.
  • belongsTo has an option to add a discriminator column and map of Model types based on the discriminated value.
  • When a model is removed that has belongsTo relationships, clear the property on the related model.
  • When a model is removed that has hasOne relationship & cascade !== false, remove the related model.
  • When a model is removed that has a hasMany relationship & cascade !== false, remove all related models.
  • When a model is removed that has a hasManyThrough relationship, remove all related relation models.

Implementation

Add a NeuroRelationship class which provides the following functions (names pending):

{
  init: function(model, database, options) {},
  relate: function(model or key) {},
  unrelate: function(model or key) {},
  set: function(models or keys) {},
  save: function(model)
}

Decouple Pub/Sub Client

Similar to Neuro.rest, add a function called Neuro.live which takes the NeuroDatabase and a callback function which is passed published messages. It should return a function which accepts messages to publish.

Neuro.live = function(database, onMessage)
{
  // implement
  return functionThatSendsMessages;
};

database has the options passed into Neuro as properties - so anything passed in as options is accessible via database.option.

Add Neuro.create

var model = Neuro.create(properties) is shorthand for:

var model = new Neuro(properties);
model.$save();

Add autoRefresh option to Neuro

When an offline event triggers, listen for an online event. Once the online event triggers call NeuroDatabase.refresh(). Ideally we should wait until all other listeners have been triggered, wouldn't want to refresh before all saves & deletes have processed.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.