Giter Club home page Giter Club logo

dstore's Introduction

dstore

The dstore package is a data infrastructure framework, providing the tools for modelling and interacting with data collections and objects. dstore is designed to work with a variety of data storage mediums, and provide a consistent interface for accessing data across different user interface components. There are several key entities within dstore:

  • Collection - This is a list of objects, which can be iterated over, sorted, filtered, and monitored for changes.
  • Store - A Store is a Collection that may also include the ability to identify, to add, remove, and update objects.

The dstore package includes several store implementations that can be used for the needs of different applications. These include:

  • Memory - This is a simple memory-based store that takes an array and provides access to the objects in the array through the store interface.
  • Request - This is a simple server-based collection that sends HTTP requests following REST conventions to access and modify data requested through the store interface.
  • Rest - This is a store built on Request that implements add, remove, and update operations using HTTP requests following REST conventions
  • RequestMemory - This is a Memory based store that will retrieve its contents from a server/URL.
  • LocalDB - This a store based on the browser's local database/storage capabilities. Data stored in this store will be persisted in the local browser.
  • Cache - This is a store mixin that combines a master and caching store to provide caching functionality.
  • Tree - This is a store mixin that provides hierarchical querying functionality, defining parent/child relationships for the display of data in a tree.
  • Trackable - This a store mixin that adds index information to add, update, and delete events of tracked store instances. This adds a track() method for tracking stores.
  • SimpleQuery - This is a mixin with basic querying functionality, which is extended by the Memory store, and can be used to add client side querying functionality to the Request/Rest store.

See the Stores section for more information these stores.

A Collection is the interface for a collection of items, which can be filtered and sorted to create new collections. When implementing this interface, every method and property is optional, and is only needed if the functionality it provides is required, however all the included stores implement every method. A collection's list of objects may not be immediately retrieved from the underlying data storage until the it is accessed through forEach(), fetch(), or fetchRange().

For more details on the Collection API and how to query, see the Collection section

A store is an extension of a collection and is an entity that not only contains a set of objects, but also provides an interface for identifying, adding, modifying, removing, and querying data. See the Store section for the details on the Store interface.

Promise-based API and Synchronous Operations

All CRUD methods, such as get, put, remove, and fetch, return promises. However, stores and collections may provide synchronous versions of those methods with a "Sync" suffix (e.g., Memory#fetchSync to fetch synchronously from a Memory store).

In addition to handling collections of items, dstore works with the dmodel package to provides robust data modeling capabilities for managing individual objects. dmodel provides a data model class that includes multiple methods on data objects, for saving, validating, and monitoring objects for changes. By setting a model on stores, all objects returned from a store, whether a single object returned from a get() or an array of objects returned from a fetch(), will be an instance of the store's data model.

For more information, please see the dmodel project.

Adapters make it possible work with legacy Dojo object stores and widgets that expect Dojo object stores. dstore also includes an adapter for using a store with charts. See the Adapters section for more information.

dstore uses Intern as its test runner. A full description of how to setup testing is available here. Tests can either be run using the browser, or using Sauce Labs. More information on writing your own tests with Intern can be found in the Intern wiki.

Dependencies

dstore's only required dependency is Dojo version 1.8 or higher. Running the unit tests requires the intern-geezer package (see the testing docs for more information). The extensions/RqlQuery module can leverage the rql package, but the rql package is only needed if you use this extension.

Contributing

We welcome contributions, but please read the contributing documentation to help us be able to effectively receive your contributions and pull requests.

License

The dstore project is available under the same dual BSD/AFLv2 license as the Dojo Toolkit.

dstore's People

Contributors

bitpshr avatar brandonpayton avatar csnover avatar dgerhardt avatar edhager avatar gerpres avatar itorrey avatar jazdw avatar kfranqueiro avatar kitsonk avatar kriszyp avatar lzhoucs avatar maier49 avatar mercmobily avatar msssk avatar neonstalwart avatar pottedmeat avatar rainmanhhh avatar twelveeighty avatar wkeese 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  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

dstore's Issues

Wrong scope for Store#parse calls in Rest store

When I created my own store based on Rest I encountered a scoping issue inside my custom parse method. At the moment, "this" cannot be used consistently to access the store because it refers to the global window object in some cases.

I fixed the scoping problem by slightly adjusting the affected Rest#get and Rest#put methods and will issue a pull request.

update test instructions

i'm trying to submit a PR to change the 'dojo/request/registry' dependency in Rest.js to 'dojo/request' but i can't get the tests to run according to the instructions.

i realized that npm install intern should be npm install intern-geezer but then i see a few 404s for rql and json-schema. are these supposed to be available too?

also, it would be preferable if it were possible to run the dstore tests by only changing things inside of the dstore directory rather than siblings - i.e. installing intern into dstore/node_modules. it's not a critical issue but it just means that i have to prepare a parent dir for dstore that is suitable for dstore development rather than developing directly from the dstore clone.

Timeout for Rest calls

Hi,

At the moment, in Rest.js all of Dojo request() calls are made without a timeout. This has the side effect of stores that never timeout -- hence, the application's little wheel will spin forever.

I was wondering if you'd be interested in a very basic pull request to add a timeout option to Rest.js so that requests can actually timeout.

The old Dojo stores also had this problem -- I guess it got inherited from there!

Thanks for listening.

Bug: cache returns old data

Hi,

The fact that Memory returns subcollections, and the way Caching works, is creating an issue with results being cached even though the data has changed.
Here is from my Chrome console:

require(['dojo/_base/declare', 'hotplate/hotDojoExtras/dstore/Memory', 'hotplate/hotDojoExtras/dstore/Cache'], function( declare, Memory, Cache ){ D = declare, M = Memory, C = Cache } );

M1 = D( [M, C], { idProperty: 'id' } )
m = new M1()
m.setData( [ {id: 0}, {id: 1}, {id: 2}, {id: 3}, {id: 4}, {id: 5}, {id: 6} ] )
> undefined

m.fetch()
> [ Object, Object, Object, Object, Object, Object, Object]

m.sort().fetch()
> [ Object, Object, Object, Object, Object, Object, Object]

m.put( { id: 8 } )
> Object {id: 8, constructor: function, getInherited: function, isInstanceOf: function, inherited: function…}

m.sort().fetch()
> [ Object, Object, Object, Object, Object, Object, Object]
// WRONG There should be 8 results here!!! 

m.sort({ p: 1 } ).fetch()
> [ Object, Object, Object, Object, Object, Object, Object, Object]
// CORRECT -- because it's for a different query

It's entirely possible I am using the Cache wrong.
Slightly related: #25 and SitePen/dgrid#950

fetchRange on a trackable memory store crash IE10

The following sample code is crashing Internet Explorer 10 (tested on Windows 7 and Windows 8, with version 0.2.0 of dstore):

require([
    "dstore/Memory",
    "dstore/Trackable"
], function (Memory, Trackable) {
    var TrackableStore = Memory.createSubclass([Trackable], {});
    var store = new TrackableStore();
    for (var i = 0; i < 100; i++) {
        store.add({label: "item " + i});
    }
    var queryResult = store.filter({});
    var tracked = queryResult;
    var tracked = queryResult.track();
    var firstRange = tracked.fetchRange({start: 0, end: 19});
    // the following is failing on IE10
    var secondRange = tracked.fetchRange({start: 20, end: 40});
});

Multiple sortBy entries in URL when doing store.sort('something').sort('else')

If you get a Rest store, and do the following:

collection = store.sort('something').sort('else');

If the store has a sortParam, when executing the query, the URL will have http://www.example.com?sortBy=+something&sortBy=+else

This is due to the code in request.js:

    _renderQueryParams: function () {
        var queryParams = [];

        arrayUtil.forEach(this.queryLog, function (entry) {
            var type = entry.type,
                renderMethod = '_render' + type[0].toUpperCase() + type.substr(1) + 'Params';

            if (this[renderMethod]) {
                push.apply(queryParams, this[renderMethod].apply(this, entry.normalizedArguments));
            } else {
                console.warn('Unable to render query params for "' + type + '" query', entry);
            }
        }, this);

        return queryParams;
    },

As you can see, the _render method is called multiple tumes -- and each time, it will add a new sortBy to the URL.

Is this the intended behaviour? I am asking because the old Dojo stores would create sortBy=+something,+else.

Before I change my code server-side, I just wanted to make sure that this "it" and it's working as intended (although it does look a little weird)

1.0.0 & events

Store.js and README.md document the "remove" and "refresh" events while "remove" has been apparently renamed to "delete" and "refresh" has been removed entirely.

Removing "refresh" is really problematic to us because now we have no standard way to know a user has called setData on a Memory store.

At least the documentation should be fixed.

This is the most inspiring piece of code ever...

Hi,

fetch: function () {
        var data = this.data;
        if (!data || data._version !== this.storage.version) {
            // our data is absent or out-of-date, so we requery from the root
            // start with the root data
            data = this.storage.fullData;
            var queryLog = this.queryLog;
            // iterate through the query log, applying each querier
            for (var i = 0, l = queryLog.length; i < l; i++) {
                data = queryLog[i].querier(data);
            }
            // store it, with the storage version stamp
            data._version = this.storage.version;
            this.data = data;
        }
        return new QueryResults(data);
    },

This code is fantastic in so many ways, I don't know where to start. Besides, you probably know it already.

Sorry about opening an issue, I didn't have a better way of contacting you all. But, I wanted to say, thank you for creating such an awesome piece of technology!

How should I use emit() as the equivalent of notify()?

Hi,

Probably a lame question... What is expected to pass to emit() when manually generating events that would normally be generated by track()?

I am still trying to find my way around Observable.js, and want to make sure I am generating correct event objects.

When a comet message lands, I publish a topic. At the moment, I am only bothering setting event.beforeId and event.target. I am missing (and could add easily enough) event.index, event.id, event.totalLength and (in case of removals) event.previousIndex.These are created by the server, so I have all of the information.

The track() function uses the internal function notify() to then emit the right event after working out positioning etc. My question is: what should tracking widgets expect from the event object? Or, even better, what I am expected to set?

In Dojo's original (and now obsolete!) stores, all you had to do was call notify() with the object. Now things are a little different...

Here is my code. Note that store.memCache is the same as store.cachingStore -- basically, when there is a comet message, I update the cache first and then emit the signal:

topic.subscribe('storeRecordUpdate', function( from, message, remote ){

  // A Comet changed a remote store. 
  if( remote ){

      // stores() is a function that will return a collection of stores            
      var storeName = message.storeName;
      var definedStores = stores( storeName );

      // The application might have several instances of the same store
      // each one with different  target parameters.
      // This will make sure only the right stores (with matching target ids) will be notified
      for( var k in definedStores ){
        var store = definedStores[ k ];
        if( objectValuesIn( store.target, store.targetHash, message.object ) ){

          var event = {}
          var params = {};

          // Sets event.target, taken from the message's object
          event.target = message.object;

          // Set event.beforeId (for the event) and params.beforeId (for the put), 
          if( typeof( message.beforeId ) !== 'undefined' ){
            event.beforeId = message.beforeId;
            params.beforeId = messge.beforeId;
          }

          // Place the item in the right spot in the cache
          store.memCache.put( message.object, params );

          // Emit the update event, which will effectively notify all tracking widgets
          // FIXME: What's the minimum/optimal requirement for the event object?
          store.emit( 'update', event );
        }
      }
    }

models break pre-existing prototype chains

while using dummy data, my custom request provider does the equivalent of lang.delegate(baseProperties, randomValues). _restore will completely replace object.__proto__ which removes the baseProperties values from the prototype chain.

Query abstraction missing

I implemented a server-side dojo/store which connects to a MongoDB. The most difficult part was actually transforming the query passed to query() into a mongoDB query. In my opinion this is difficult because of the lack of a query abstraction in dojo/store.

Actually I had to adapt my store implementation to the clients using the store. Specifically diji/form/FilteringSelect and gridx. Both have different ways to build queries. Looking at the comments in dijit/form/FilteringSelect the lack of abstraction becomes evident. The comment mentions two Stores that were specifically handled (JsonRest and Memory). The problem arises because a like-query is not standardized. Similary Gridx creates very complex queries with logical operators which cannot be expressed by simple http-parameters

I suggest you introduce a query abstraction like rql or MongoDb's query in dstore. That makes store and client implementors' lifes easier.

Is it actually good to create a new collection after sort(), range()?

Hi guys,

I am diving more and more into the code, and wondering: in a world where we use Memory.js as default caching, does it make sense to have the Memory store return a subcollection whenever we sort(), range() or filter()?
I am asking because:

  • I reported SitePen/dgrid#950 -- having a sub-collection here creates a problem
  • In my application, I tend to pre-load the store's cache for config stores that are always needed. Injection is obviously hard at best since a new store, with its own cache, is returned every time you apply sort, range, filter
  • In a context where the memory store is used for caching, it just seems dangerous to end up effectively having multiple stores caching each other when dealing with a Memory store

Can you guys tell me what I am missing here? (because normally when I come up with stuff like this I am mercilessly proven wrong, and don't want to add annoying, unnecessary noise to dstore's development)

Found one...

:-)
Jan

diff --git package.json package.json
index dfc3bf5..a571118 100644
--- package.json
+++ package.json
@@ -1,5 +1,5 @@
 {
-  "name": "dgrid",
+  "name": "dstore",
   "author": "Kris Zyp",
   "version": "0.1.0",
   "description": "A data collection and modelling infrastructure",

Dstore and positioning: tutorial outline

Hi people,

I am writing a tutorial to explain "placement" in dstore. This is aimed at people writing the server side of the story.
This is the "factual" outline of what I will explain. Can you please OK all this? I am 99.9% sure I didn't write anything stupid, but I would love to be double-checked!

I am also writing another tutorial where I explain pretty much every possible use-case scenario when using stores (cached, un-cached, with filtering on, etc.) explaining the (rare) cases where a refresh is needed. But that's a different story.

STORES AND POSITIONING

  • Natural ordering is where things are placed when querying without specifying sorting

  • Natural ordering should always exist for stores (although it could be switched off on server to optimise)

  • An item's placement is set by

    • the beforeId parameter in put() (existing and new elements)
    • or at the end unless the store's defaultNewToStart is set to true (new elements)
  • In Memory.js, the JS library has access to the store; so, when adding to Memory without beforeId, Memory checks for the store's defaultNewToStart attribute to see where to place a new element. When beforeId is set to null, then defaultNewToStart is ignored and element is added at the end.

  • In Rest.js, two headers are set depending on put's options.beforeId and the store's defaultNewToStart.

    • If beforeId is set:
      • If it's !== null, X-Put-Before will be set to beforeId;
      • If it's == null, X-Put-Default-Position will be set to end.
    • If beforeId is not set:
      • If it's a new element, then X-Put-Default-Position is set to start or end (depending on the store's defaultNewToStart).

    Note that X-Put-Default-Position is used BOTH if beforeId is null (to make up for the inability to pass anything but a string via headers), AND for new elements where beforeId is not set (to tell the server where to place the element in such a case, to make up for the fact that the server doesn't know what the client store's default is).

  • The server doesn't know anything about the store; for positioning, two headers are used" X-Put-Before and X-Put-Default-Position. If the server tracks natural ordering for the collection, it will

    1. Check if X-Put-Before is set. If so, will need to place the item there
    2. OR check X-Put-Default-Position -- if it's end, it will place things at the end. If it's start, it will place the item as first. If it's a new element, and neither one of the headers is set, then will place at the end (the default)

Memory store does not set ids of items passed in constructor options

I think there is an inconsistency between passing items to the constructor, for example

var store = new Memory({data:items});

and adding items one by one through

store.add(item);

In the second case, an id is automatically assigned to the item if it did not already have one, while in the first case no ids are set.

It is not quite clear to me what the contract is exactly with respect to ids, maybe I missed something...

introducing dojo/_base/declare depedency

In dojo 1.x from a user standpoint it was possible to use stores without requiring dojo/_base/declare (like adding Observable feature on a existing store did not require bringing in declare). Thus allowing easy integration of stores with projects using other OOP paradigms (like dcl) because the app developper did not have to "know" about declare.

In dstore, while making the project independant of dojo core, it actually started depending more on dojo/_base/declare (which is I guess expected to be deprecated in 2.x). This sounds like a strange move that point in time even though obviously I understand that rationale for that as well.

Would there be a way to have dstore being agnostic and work with any OOP one wants to use or at least to be "back" to 1.X state where one did not have to use dojo/_base/declare to do simple things like observing a store?

Get raw item from Model

Hi,

I am wondering the best way to get the raw item from a Model eg an object that doesnt have the additional methods etc, just the original item content added to a store.

Thanks

Filtering by query and filtering by range do not have the same consequence on Observable notifications

Basically if I modify, remove, ... an element that is filtered by query, notifications from Observable will provide undefined indexes letting me know they are out of my scope. If I modify, remove, ... an element that is filtered by being outside of the range I still get the notifications with "some" index which is neither an index in the original collection (i.e. before filtering) or after (as anyway it is not in the collection after).

This seems inconsistent to me and also this is causing issue when you don't know you are working on a "ranged" collection. (it was passed to you).

Is that an expected behavior? If no, can this be fixed? If yes, what is the recommendation here to deal with those cases in tracking (I did not find Observable documentation)? Do you agree that ideally I should not have to know the collection has been ranged to deal with notifications?

Example:

var S = declare([Memory, Observable], {});
var s = new S({
        data: [
            { id: "foo", name: "Foo" },
            { id: "bar", name: "Bar" }
        ]
    }).filter({}).range(1).track();

s.on("update", function (event) {
    console.log("previousIndex: "+event.previousIndex);
    console.log("index: "+event.index);
});

s.on("remove", function (event) {
    console.log("previousIndex: "+event.previousIndex);
    console.log("index: "+event.index);
});

s.put({ id: "foo", name: "Foo0" });
s.remove("foo");

s = new S({
        data: [
            { id: "foo", name: "Foo" },
            { id: "bar", name: "Bar" }
        ]
    }).filter({id: "bar"}).track();

s.on("update", function (event) {
        console.log("previousIndex: "+event.previousIndex);
        console.log("index: "+event.index);
});

s.on("remove", function (event) {
        console.log("previousIndex: "+event.previousIndex);
        console.log("index: "+event.index);
});

s.put({ id: "foo", name: "Foo0" });
s.remove("foo");

Thanks.

When removing an item for a [Observable, Rest] store, event.previousIndex is always undefined

This problem is visible when using the list widget with an observable Rest store.

var ObservableRest = declare([Observable, Rest], {});
list.store = new ObservableRest({target: "http://..."});

You can see that, unlike with a [Observable, Memory] store, the list is not update when a List.store.add()/List.store.remove() methods are called.

I didn't dig into this any further but I noticed that event.previousIndex/event.index were always undefined, causing itemAdded/itemRemoved to never execute.

canceling requests shows unhandled rejections

if i cancel a request (using dstore/Request) i'll get the error stack from the promise instrumentation complaining that there's an unhandled rejection. i think it's the response promise in Request#_request that has no handler and isn't exposed so none can be added.

should there be an error handler in dstore and/or should it expose response?

also, what do i do if i want to try to do something in response to "transport" errors - i.e. how can i do the equivalent of inspecting response.response?

Case insensitive filtering and sorting

I realise there is a discussion going on in terms of expanding the querying abilities in dstore #34 . I have added my humble 2c to that discussion.

However, I am having a practical issue now where I cannot even really complete the test cases for my store tutorial as my DB layer is case insensitive in matching and sorting, whereas Memory does care about case. This gave me some interesting head-scratching moments where a newly added element in a cache collection ended up in a seemingly random spot in the dgrid (because the queryEngine was placing it considering case sensitiveness).
While I think it's great that a direction in terms of querying is being taken, I was hoping you'd consider a pull request to make queries and sorting case-sensitive now (according to the store's corresponding attributes).

I realise that there is an ongoing discussion in #34 in terms of query abstraction layer. But, at the moment I am not even able to complete a very simple use case, as my DB server is case-insensitive in filtering and sorting, whereas Memory.js does care about case. This created an interesting head-scratching moment when queryEngine places items in seemingly random spots.

I meant to create a PR with this, but Github kept on saying "there is nothing to compare". However, the changed file is:

https://github.com/mercmobily/dstore/blob/caseinsensitive/objectQueryEngine.js

The list of changes:

mercmobily#1

It has very small changes, aimed at making filtering and sorting case-insensitive based on store's configuration.

Thanks as ever.

When using MemoryStore, transaction() is undefined

Inexplicable undefined transction() function on Store. When using dStore, I can access functions such as "get", "put", "remove", etc on a store, but "transaction" is undefined. Also, I can't even see Collection.js being loaded, which is odd, since I'm using fetch(), and I thought that returned a collection. How can I return a collection without a collection object?

QueryResults.forEach callback parameters not as expected

Hi
Just a little thing, just started using the latest release which has introduced QueryResults. The forEach function calls the provided callback with the parameters -
data[i], data
whereas the normally expected signature for a forEach callback would have the index as the second parameter i.e. -
data[i], i, data

Loving the whole dstore thing, nice work!

Regards

Mark

Publish a pre-release to bower

In order for other projects to rely on what is already here it would be nice not waiting 1.0 release an tag the repo with a pre-release tag (0.1.0-dev or something like that). That way it would be possible to create a bower dependency from any project to that pre-release.

dirty indicator for Model

this is a feature request to add some kind of dirty indicator. the most basic algorithm would be that the object is clean when:

  • it comes from the store
  • is created
  • is saved

and then any change after that makes it dirty until the next save.

a more sophisticated algorithm would track the most recently saved values and then dirty would depend on whether or not the data was in the same state. this would also allow to revert to the saved state.

the basic algorithm would be useful for things like enabling/disabling a save button and preventing a user from navigating away without saving.

maybe the basic algorithm could be included in the Model and perhaps if the more complex algorithm is ever implemented, it could be an optional mixin/subclass since it may have more of an impact on performance.

i may be able to put some time into making the basic algorithm happen if i can get a hint about how to approach it.

Question on dstore's code (observable)

Hi,

A couple of questions:

if (this.queryEngine) {
  arrayUtil.forEach(this.queryLog, function (entry) {
    // TODO: This isn't extensible for new query types. How we can we make a general determination to not include a query type as we do for 'range'?
      if (entry.type !== 'range') {

type can no longer be "range", right? (there are now two different functions for fetching, fetch() and fetchRange()

https://github.com/SitePen/dstore/blob/master/Observable.js#L175-L176

1a) Could you add a comment describing what the code above actually does? By reading it, it creates a queryExecutor function based on the queryLog; however, I don't quite understand how this function will get to be used: what parameter do you pass it? An object for searching? Or an array for sorting? I am a little lost

  1. In Observable again, we have:
                if (type === 'add' || type === 'update') {
                    if (queryExecutor) {
                        // with a queryExecutor, we can determine the correct sorted index for the change

                        if (queryExecutor.matches ? queryExecutor.matches(target) :
                                queryExecutor([target]).length) {

Me not quite understanding how to use queryExecutor is not helping me here. But, isn't matches an echo from the previous implementation of stores? Plus, in my tests the call to queryExecutor.matches(target) always returns an array with the one record passed as target. I am a little confused!

https://github.com/SitePen/dstore/blob/master/Observable.js#L262-L264

Issue porting existing app to dstore: asking for infinity, observer failing

Hi,

Please only respond and close if it's easy to solve. Otherwise, please just close it.
I am porting my app to dstore -- which is proving harder than expected.

At the moment, dgrid is asking for:

Range:items=0-Infinity

Which is strange, because it's an onDemandList and I would expect it to ask for the first 25 items. I fixed my server so that the problem is no longer there. But then, whenever I do a successful PUT, Observable fails at line 229: https://github.com/SitePen/dstore/blob/master/Observable.js#L229 The problem is that range.count is 25 (?), whereas resultArray is only (say) 7 elements. So, the minute it tries to check for the 8th element, store.getIdentity bombs because object is undefined.

This is a normal store, created as:

var StoreConstructor = declare( [ Observable, Rest, Cache  ], {
      idProperty: idProperty,
      target: resolvedTarget,
      sortParam: definedStore.sortParam,
      useRangeHeaders: true,
    } );

And it's a normal dgrid:

var List = declare( [ OnDemandList, Selection, Keyboard, DijitRegistry, DnD ] 
var list = new List( { collection: collection, sort: initialSort, queryOptions: initialQueryOptions, query: initialQuery, region: 'center' }

Am I doing anything blatantly stupid? For now, I am just adding this to the cycle:

if( ! object) continue;

But if you have any quick advice on this I will be very grateful, as I have been banging my head against it for far too long...

What is the status of the refresh event?

Memory.setData() is sending a refresh event, however I don't find any documentation for it. Should that event be considered "official" and be listened to by dstore Observable consumers like add, remove and update?

Empty model?

The model property documentation states:

 One can set this to an empty object if you don't want any methods to decorate the returned objects (this can improve performance by avoiding prototype setting)

but if I set it to {} I get the following error:

Error: Cannot set property '_store' of undefined

Memory: updates reported with no index or previousIndex

dstore 0.1.0 reported updates where index === previousIndex === a number (the item's index), but with dstore 0.1.1 updates are reported as index === previousIndex === undefined.

From this code in Memory.js, it looks like the index and previousIndex are available as i and previousIndex. Yet they are not passed into emit():

// the fullData has been changed, so the index needs updated
var i = isFinite(previousIndex) ? Math.min(previousIndex, destination) : destination;
for (var l = data.length; i < l; ++i) {
    index[this.getIdentity(data[i])] = i;
}

this.emit(eventType, event);

Later we get down to this code in Observable.js:

// without data, we have no way to determine the indices effected by the change,
// so just pass along the event and return.

Found this bug from our line 42 of our "Updates" test case https://github.com/wkeese/delite/blob/dstore/tests/unit/Store.js#L42, which updates an existing item, and then eventually our callback gets an event with index === undefined.

Am I doing something wrong?

Custom Store and excludePropertiesOnCopy

Imagine I have my own Store subclass and don't want to copy some of the properties when creating a sub collection just as Store.excludePropertiesOnCopy is doing. In my be useful having that property be protected so that the subclass can add to it.

Property Objects: "default" shouldn't be default: reserved ECMAScript keyword.

I really much like dstore. Having worked with Sencha I would be happy to see dstore in Dojo. Personally, though, I don't feel comfortable with using "default" to denote default values in property objects. Its a reserved ECMAScript keyword and "may not be used as identifier". I think many people will routinely omit the quotation marks which are required to make it a valid property name. Depending on their toolchain they could eventually run into weired issues with JavaScript compilers/transpilers, code validators like JSLint or editors (see examples below).

So I think the default shouldn't be "default". There should be some alias at least, something like "defaultsTo" or "initWith". Even "value" would be okay since it is not too hard to guess, that its meant to be a "default value" when it is used in a schema definition.

Examples for the variety of issues with "default" being used as property name:
[1] http://stackoverflow.com/questions/6152344/youtube-uses-default-as-a-name-for-an-object-property
[2] http://jslinterrors.com/expected-an-identifier-and-instead-saw-a-a-reserved-word,
[3] http://learningswhileworking.blogspot.de/2010/05/javascript-default-key-word-problem.html
[4] jdavisclark/JsFormat#89

Question: tracking the results of a query on a store

Hi,

I still have trouble to figure out the collection and store tracking detailed specification from the current documentation (that, I understand, is a work in progress), hence my question.

Suppose the following sample code:

var store = ... // Observable store
var result = store.filter(...).sort(...).range(...);
result.track();

after this last line, does result become "live" and is guaranteed to be updated every time
the content of the store is ?

Slight bug for unsorted filtered store with defaultNewToStart set to true

While trying out all possible test cases with stores, I found that a filtered store that has defaultNewToStart set as true will notify (in Observable) new elements as to be placed at the END of the dataset. This is even though the caching store has placed the element at the start (as required) -- and ditto for the DB server.

The problem is that in such a case the flow gets to line 290 of Observable: https://github.com/SitePen/dstore/blob/master/Observable.js#L290

candidateIndex = sampleArray.length;

The reason why it gets to this line is that since there is filtering, queryExecutor is defined and therefore it starts using it to work out where the element should be. At this point, only beforeId is used to place the element anywhere other than the end.

The solution is to change line 290 into:

candidateIndex = store.defaultNewToStart ? 0 : sampleArray.length;

However, I am not sure about something... see ADDENDUM.

ADDENDUM:
This rings a little strange to me) that if there is no filtering, the if in line 259 fails https://github.com/SitePen/dstore/blob/master/Observable.js#L259 :

if (queryExecutor) {

This means that stores without queries are basically treated the same as stores without any kind of queryEngine, since queryExecutor depends both on the presence of a queryEngine, and the presence of some kind of querying.

Dojo-compliant, temporary stores to run tests

Hi there,

I have finished developing the 12 stores where I cover all of the cases (ordered, unordered, filtered, unfiltered, defaultNewToStart true/false). It's 6 cached stores, and 6 uncached stores. I cover all of the possible use cases for the stores, which is nice. By doing that, I found #45 which was handy.

My question is: would it be helpful if I created a "store testing" application, that worked like that:

  1. You ask for a URL of this sort: http://example.com/createStores/SOME_UNIQUE_ID

  2. For 5 minutes, you have a bunch of stores you can use for testing; the stores will be 100% compatible with dstore (and therefore dgrid)

  3. I was thinking about some typical stores:

  • An easy "people" one, with "name, surname, age"
  • A store with a flag, like "name, active"
  • Anything else?
  1. After 5 minutes, the data is wiped from the stores and that unique ID is no longer usable

I think it would be a great way for people to test stores and see dgrid's potential without setting up a whole server.

Have you already thought of it? (Or maybe even done it?) Do you think it would be useful/helpful?

Observable, natural ordering, and REST

Hi,

My goal is to make sure that an ordered Rest store, with or without Memory as cache, is fully Observable. Here is the comprehensive list of mental notes I had while making this happen. I will be as brief as possible. #6 is "slightly related".

Observed, Memory store

This is the simplest case.

My pull request #22 should actually be all you need to make this reality. The queryEngine should find the items without any trouble when the list is naturally ordered, since the items are placed within the array in their natural order.

Observed Rest store (no caching)

This is a much harder issue. I battled it a lot in Dojo's implementation of stores (which are nowhere near as advanced as dstore!). The main issue we all know is that with Rest you don't have access to the local data.
In the end, there are two cases:

  1. The list is naturally ordered and there is no filtering. In this case, before should be followed. Why no filtering? Because you might well do a put( { name: 'Tony', enabled: false}, { before: chiara } ) -- if there is filtering, and you are filtering for enabled: true, the last thing you want is placing that element before chiara and display it in your dgrid

  2. Any other case (the list is ordered, or there is filtering). In this case, there is simply NO way for the store to know where an item is placed. The current way of dealing with it is "leave it in place". This is bad for a number of reasons. You could be looking at a sorted list, and you might well change an attribute that defines sorting. Or you might change an attribute and cause it to be filtered out. And so on.

I really believe that there should be an event, called refresh, which indicates that the item's position simply couldn't be worked out. That way, widgets will simply have no choice but refresh their list, to make sure that the changed item is placed (or misplaced) correctly. This would also make sure that multiple dgrids using the same store will all get the refresh event.

There is a case for defaultPlacement. Right now, I see defaultPlacement as an attribute of the store. I feel that's a potentially bad idea: you might have two dgrids using the same store, one using natural ordering and the other sorting by name. Having a defaultPlacement in the store itself would mean that the item is always placed last or first. Plus, you don't even know if the item is even meant to be displayed (see: filters should be applied). I think that forcePlacement should be an attribute of the put() call, and should "force" placement of an item. The rational is that a specific put() call will know if it's appropriate to force the placement at the end.

Observed, CACHED Rest store

At least in theory, cached Rest stores should be good to go. In Dojo's implementation, Rest stores gained the Memory store's queryEngine, and used that to work out placement.

In practice, I have the feeling that this use case should be the same as the uncached one though, with the refresh event type being issued when there is no placement indication before or forcePlacement are not set or if there is sorting of filtering for the store.

Here is my reasoning.

In dstores, things are different to Dojo because the Memory store is no longer composite with the Rest store, but it's an "attribute" (cachingStore). So, there is no queryEngine to be used.

(BTW, using Memory's queryEngine for a Rest store had its big problems regardless. For example, filtering a field might have slightly different meanings in Rest and Memory, with Rest looking for partial matches by default for example. I have never been a fan of using Memory's queryEngine to decide whether an item should be included in a result, although it works most of the time)

One issue with events: when using track(): it's important to remember what is issuing the event exactly. For example, with a cached Observable Rest store, the emitting store will be the Rest one, which is also the "main" one. So, the emit will happen before the memory store's put() has a chance to be called. A possible solution I tried was to make sure that Cache.js ALSO emitted the right event.

I am still not 100% confident on how Observer works. I am not quite sure how the queryLog works, and these lines in Observer.js leave me baffled: https://github.com/SitePen/dstore/blob/master/Observable.js#L168-L180 What I can see, is that partialData is indeed set, BUT (and this is not good) even with Memory.js that places items in the right spot, partialData STILL has a copy of the items in the old sort order. I tried to understand exactly what was going on, but in the end I couldn't quite get it (sorry).

To repeat what I wrote at the beginning: " I have the feeling that this use case should be the same as the uncached one though, with the refresh event type being issued when there is no placement indication before or forcePlacement are not set or if there is sorting of filtering for the store."

I hope this helps...

Support for composite keys

Store.get(id) and Store.remove(id) do not support composite keys. Model.save() and Model.remove() probably use the primary key, too. Perhaps it isn't worth it, but if it is, it would be good to think about it before releasing 1.0. Django has been struggling for years to add composite key support since it wasn't considered in the initial design.

Rest.map() and .filter() trigger an error in chrome

On chrome, if I do for example:

var R = new Rest({target: "http://localhost:1337/book/"})
R.map(function(){return null});

I get

Refused to get unsafe header "Content-Range" 

It might be an issue with my server's headers but the fact that I don't get it on firefox and ie10 makes me doubt.

Rest.put fails when creating an new object

I'm having a problem while using the put method from Rest.js.

var R = new Rest({target: "http://localhost:1337/book/"})
R.put({id: 90, title: "my new book"}).then(
   function(res){ console.log("it worked")}, 
   function(res){ console.log("it failed")}
)

This makes a PUT request to http://localhost:1337/book/90, and updates correctly the element of id 90 if it exists. If it doesn't, the server returns 404. But instead of creating object, .put() raises an exception.

PUT http://localhost:1337/book/90 404 (Not Found) 

I might be mistaken but I expect this exception to be handled by .put() by making then the proper PUT request to create the object, ie a PUT request to http://localhost:1337/book/.

missing json-schema dependency when building

when running a build (without having json-schema available) i get the error:

error(311) Missing dependency. module: dstore/extensions/jsonSchema; dependency: json-schema/lib/validate

this is expected of course but if i'm not using dstore/extensions/jsonSchema then i'd like a simple way to prevent the build from calling this an error. i can inline something in my profile to ignore that module:

{
    name: 'dstore',
    resourceTags: {
        ignore: function (filename, mid) {
            return /jsonSchema$/.test(mid);
        }
    }
},

but is there a better way to make (what i think is) the most common use case simpler?

this is not a high priority for me but consider it a nudge to take a look at how dstore builds if you haven't had the chance to yet.

'remove' event needs full object too

Hi,

At the moment, when we observe a store and there is a delete, only the id of the item is emitted:

// For example, memory.js
this.emit('remove', {id: id});

However, in my application I have a real use case where I observe a store, and I want to take action when an item is deleted; the action depends on the item's contents.

(It's about authentication: each button observes the store, and if that method gets deleted, then the button becomes unticked)

To do this, I actually need the object itself in the remove event -- not just its ID. You can do a quick get if you need it, but it's not practical -- plus, I am cache-less at the moment because of other tickets...

Any real reason why the object is not included? It's handy! (And if you have it, you don't have to constantly check if the type is remove and expect something other than an object n target...)

Thanks!

parent properties and event bubbling?

Hi,

are you planing to support a parent property, so that navigating to the root of the object graph is possible? Are you further planning on supporting event bubbling in that way?

gform recently introduced Models that are pretty similar to dstore's:

The gform Models support bubbling of value and state change events. Thus each property knows about the count of its errors and its children's errors. Also it's possible to listen to changes on the root. Once a validation is started at the root event bubbling is disabled for the duration. Probably the changes that trigger event bubbling should be restrictable.

The gform Models also has a 'visit' methods to visit the object graph. That is useful for adding extra functionality without having to extend all Models.

very interested in this exciting project
Stefan

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.