From d912447176ca25a09c6cfe10827c1c65cce84341 Mon Sep 17 00:00:00 2001 From: Jason Dobry Date: Tue, 13 May 2014 21:40:29 -0600 Subject: [PATCH 1/3] Removed overly nested source file organization. --- Gruntfile.js | 4 ++-- src/adapters/{http/index.js => http.js} | 0 src/datastore/async_methods/{create/index.js => create.js} | 0 src/datastore/async_methods/{destroy/index.js => destroy.js} | 0 .../async_methods/{destroyAll/index.js => destroyAll.js} | 0 src/datastore/async_methods/{find/index.js => find.js} | 0 src/datastore/async_methods/{findAll/index.js => findAll.js} | 0 src/datastore/async_methods/{refresh/index.js => refresh.js} | 0 src/datastore/async_methods/{save/index.js => save.js} | 0 src/datastore/sync_methods/{changes/index.js => changes.js} | 0 .../{defineResource/index.js => defineResource.js} | 0 src/datastore/sync_methods/{digest/index.js => digest.js} | 0 src/datastore/sync_methods/{eject/index.js => eject.js} | 0 src/datastore/sync_methods/{ejectAll/index.js => ejectAll.js} | 0 src/datastore/sync_methods/{filter/index.js => filter.js} | 0 src/datastore/sync_methods/{get/index.js => get.js} | 0 .../sync_methods/{hasChanges/index.js => hasChanges.js} | 0 src/datastore/sync_methods/{inject/index.js => inject.js} | 0 .../sync_methods/{lastModified/index.js => lastModified.js} | 0 .../sync_methods/{lastSaved/index.js => lastSaved.js} | 0 src/datastore/sync_methods/{previous/index.js => previous.js} | 0 src/{errors/index.js => errors.js} | 0 src/{utils/index.js => utils.js} | 0 .../async_methods/{create/index.test.js => create.test.js} | 0 .../async_methods/{destroy/index.test.js => destroy.test.js} | 0 .../{destroyAll/index.test.js => destroyAll.test.js} | 0 .../async_methods/{find/index.test.js => find.test.js} | 0 .../async_methods/{findAll/index.test.js => findAll.test.js} | 0 .../async_methods/{refresh/index.test.js => refresh.test.js} | 0 .../async_methods/{save/index.test.js => save.test.js} | 0 .../sync_methods/{changes/index.test.js => changes.test.js} | 0 .../{defineResource/index.test.js => defineResource.test.js} | 0 .../sync_methods/{eject/index.test.js => eject.test.js} | 0 .../sync_methods/{ejectAll/index.test.js => ejectAll.test.js} | 0 .../sync_methods/{filter/index.test.js => filter.test.js} | 0 .../datastore/sync_methods/{get/index.test.js => get.test.js} | 0 .../{hasChanges/index.test.js => hasChanges.test.js} | 0 .../sync_methods/{inject/index.test.js => inject.test.js} | 0 .../{lastModified/index.test.js => lastModified.test.js} | 0 .../{lastSaved/index.test.js => lastSaved.test.js} | 0 .../sync_methods/{previous/index.test.js => previous.test.js} | 0 41 files changed, 2 insertions(+), 2 deletions(-) rename src/adapters/{http/index.js => http.js} (100%) rename src/datastore/async_methods/{create/index.js => create.js} (100%) rename src/datastore/async_methods/{destroy/index.js => destroy.js} (100%) rename src/datastore/async_methods/{destroyAll/index.js => destroyAll.js} (100%) rename src/datastore/async_methods/{find/index.js => find.js} (100%) rename src/datastore/async_methods/{findAll/index.js => findAll.js} (100%) rename src/datastore/async_methods/{refresh/index.js => refresh.js} (100%) rename src/datastore/async_methods/{save/index.js => save.js} (100%) rename src/datastore/sync_methods/{changes/index.js => changes.js} (100%) rename src/datastore/sync_methods/{defineResource/index.js => defineResource.js} (100%) rename src/datastore/sync_methods/{digest/index.js => digest.js} (100%) rename src/datastore/sync_methods/{eject/index.js => eject.js} (100%) rename src/datastore/sync_methods/{ejectAll/index.js => ejectAll.js} (100%) rename src/datastore/sync_methods/{filter/index.js => filter.js} (100%) rename src/datastore/sync_methods/{get/index.js => get.js} (100%) rename src/datastore/sync_methods/{hasChanges/index.js => hasChanges.js} (100%) rename src/datastore/sync_methods/{inject/index.js => inject.js} (100%) rename src/datastore/sync_methods/{lastModified/index.js => lastModified.js} (100%) rename src/datastore/sync_methods/{lastSaved/index.js => lastSaved.js} (100%) rename src/datastore/sync_methods/{previous/index.js => previous.js} (100%) rename src/{errors/index.js => errors.js} (100%) rename src/{utils/index.js => utils.js} (100%) rename test/integration/datastore/async_methods/{create/index.test.js => create.test.js} (100%) rename test/integration/datastore/async_methods/{destroy/index.test.js => destroy.test.js} (100%) rename test/integration/datastore/async_methods/{destroyAll/index.test.js => destroyAll.test.js} (100%) rename test/integration/datastore/async_methods/{find/index.test.js => find.test.js} (100%) rename test/integration/datastore/async_methods/{findAll/index.test.js => findAll.test.js} (100%) rename test/integration/datastore/async_methods/{refresh/index.test.js => refresh.test.js} (100%) rename test/integration/datastore/async_methods/{save/index.test.js => save.test.js} (100%) rename test/integration/datastore/sync_methods/{changes/index.test.js => changes.test.js} (100%) rename test/integration/datastore/sync_methods/{defineResource/index.test.js => defineResource.test.js} (100%) rename test/integration/datastore/sync_methods/{eject/index.test.js => eject.test.js} (100%) rename test/integration/datastore/sync_methods/{ejectAll/index.test.js => ejectAll.test.js} (100%) rename test/integration/datastore/sync_methods/{filter/index.test.js => filter.test.js} (100%) rename test/integration/datastore/sync_methods/{get/index.test.js => get.test.js} (100%) rename test/integration/datastore/sync_methods/{hasChanges/index.test.js => hasChanges.test.js} (100%) rename test/integration/datastore/sync_methods/{inject/index.test.js => inject.test.js} (100%) rename test/integration/datastore/sync_methods/{lastModified/index.test.js => lastModified.test.js} (100%) rename test/integration/datastore/sync_methods/{lastSaved/index.test.js => lastSaved.test.js} (100%) rename test/integration/datastore/sync_methods/{previous/index.test.js => previous.test.js} (100%) diff --git a/Gruntfile.js b/Gruntfile.js index 552625e..e676d35 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -76,8 +76,8 @@ module.exports = function (grunt) { options: { alias: [ 'lib/observe-js/observe-js.js:observejs', - 'src/errors/index.js:errors', - 'src/utils/index.js:utils' + 'src/errors.js:errors', + 'src/utils.js:utils' ], // TODO: There's got to be a better way to consume observe-js without it polluting the global space postBundleCB: function (err, src, next) { diff --git a/src/adapters/http/index.js b/src/adapters/http.js similarity index 100% rename from src/adapters/http/index.js rename to src/adapters/http.js diff --git a/src/datastore/async_methods/create/index.js b/src/datastore/async_methods/create.js similarity index 100% rename from src/datastore/async_methods/create/index.js rename to src/datastore/async_methods/create.js diff --git a/src/datastore/async_methods/destroy/index.js b/src/datastore/async_methods/destroy.js similarity index 100% rename from src/datastore/async_methods/destroy/index.js rename to src/datastore/async_methods/destroy.js diff --git a/src/datastore/async_methods/destroyAll/index.js b/src/datastore/async_methods/destroyAll.js similarity index 100% rename from src/datastore/async_methods/destroyAll/index.js rename to src/datastore/async_methods/destroyAll.js diff --git a/src/datastore/async_methods/find/index.js b/src/datastore/async_methods/find.js similarity index 100% rename from src/datastore/async_methods/find/index.js rename to src/datastore/async_methods/find.js diff --git a/src/datastore/async_methods/findAll/index.js b/src/datastore/async_methods/findAll.js similarity index 100% rename from src/datastore/async_methods/findAll/index.js rename to src/datastore/async_methods/findAll.js diff --git a/src/datastore/async_methods/refresh/index.js b/src/datastore/async_methods/refresh.js similarity index 100% rename from src/datastore/async_methods/refresh/index.js rename to src/datastore/async_methods/refresh.js diff --git a/src/datastore/async_methods/save/index.js b/src/datastore/async_methods/save.js similarity index 100% rename from src/datastore/async_methods/save/index.js rename to src/datastore/async_methods/save.js diff --git a/src/datastore/sync_methods/changes/index.js b/src/datastore/sync_methods/changes.js similarity index 100% rename from src/datastore/sync_methods/changes/index.js rename to src/datastore/sync_methods/changes.js diff --git a/src/datastore/sync_methods/defineResource/index.js b/src/datastore/sync_methods/defineResource.js similarity index 100% rename from src/datastore/sync_methods/defineResource/index.js rename to src/datastore/sync_methods/defineResource.js diff --git a/src/datastore/sync_methods/digest/index.js b/src/datastore/sync_methods/digest.js similarity index 100% rename from src/datastore/sync_methods/digest/index.js rename to src/datastore/sync_methods/digest.js diff --git a/src/datastore/sync_methods/eject/index.js b/src/datastore/sync_methods/eject.js similarity index 100% rename from src/datastore/sync_methods/eject/index.js rename to src/datastore/sync_methods/eject.js diff --git a/src/datastore/sync_methods/ejectAll/index.js b/src/datastore/sync_methods/ejectAll.js similarity index 100% rename from src/datastore/sync_methods/ejectAll/index.js rename to src/datastore/sync_methods/ejectAll.js diff --git a/src/datastore/sync_methods/filter/index.js b/src/datastore/sync_methods/filter.js similarity index 100% rename from src/datastore/sync_methods/filter/index.js rename to src/datastore/sync_methods/filter.js diff --git a/src/datastore/sync_methods/get/index.js b/src/datastore/sync_methods/get.js similarity index 100% rename from src/datastore/sync_methods/get/index.js rename to src/datastore/sync_methods/get.js diff --git a/src/datastore/sync_methods/hasChanges/index.js b/src/datastore/sync_methods/hasChanges.js similarity index 100% rename from src/datastore/sync_methods/hasChanges/index.js rename to src/datastore/sync_methods/hasChanges.js diff --git a/src/datastore/sync_methods/inject/index.js b/src/datastore/sync_methods/inject.js similarity index 100% rename from src/datastore/sync_methods/inject/index.js rename to src/datastore/sync_methods/inject.js diff --git a/src/datastore/sync_methods/lastModified/index.js b/src/datastore/sync_methods/lastModified.js similarity index 100% rename from src/datastore/sync_methods/lastModified/index.js rename to src/datastore/sync_methods/lastModified.js diff --git a/src/datastore/sync_methods/lastSaved/index.js b/src/datastore/sync_methods/lastSaved.js similarity index 100% rename from src/datastore/sync_methods/lastSaved/index.js rename to src/datastore/sync_methods/lastSaved.js diff --git a/src/datastore/sync_methods/previous/index.js b/src/datastore/sync_methods/previous.js similarity index 100% rename from src/datastore/sync_methods/previous/index.js rename to src/datastore/sync_methods/previous.js diff --git a/src/errors/index.js b/src/errors.js similarity index 100% rename from src/errors/index.js rename to src/errors.js diff --git a/src/utils/index.js b/src/utils.js similarity index 100% rename from src/utils/index.js rename to src/utils.js diff --git a/test/integration/datastore/async_methods/create/index.test.js b/test/integration/datastore/async_methods/create.test.js similarity index 100% rename from test/integration/datastore/async_methods/create/index.test.js rename to test/integration/datastore/async_methods/create.test.js diff --git a/test/integration/datastore/async_methods/destroy/index.test.js b/test/integration/datastore/async_methods/destroy.test.js similarity index 100% rename from test/integration/datastore/async_methods/destroy/index.test.js rename to test/integration/datastore/async_methods/destroy.test.js diff --git a/test/integration/datastore/async_methods/destroyAll/index.test.js b/test/integration/datastore/async_methods/destroyAll.test.js similarity index 100% rename from test/integration/datastore/async_methods/destroyAll/index.test.js rename to test/integration/datastore/async_methods/destroyAll.test.js diff --git a/test/integration/datastore/async_methods/find/index.test.js b/test/integration/datastore/async_methods/find.test.js similarity index 100% rename from test/integration/datastore/async_methods/find/index.test.js rename to test/integration/datastore/async_methods/find.test.js diff --git a/test/integration/datastore/async_methods/findAll/index.test.js b/test/integration/datastore/async_methods/findAll.test.js similarity index 100% rename from test/integration/datastore/async_methods/findAll/index.test.js rename to test/integration/datastore/async_methods/findAll.test.js diff --git a/test/integration/datastore/async_methods/refresh/index.test.js b/test/integration/datastore/async_methods/refresh.test.js similarity index 100% rename from test/integration/datastore/async_methods/refresh/index.test.js rename to test/integration/datastore/async_methods/refresh.test.js diff --git a/test/integration/datastore/async_methods/save/index.test.js b/test/integration/datastore/async_methods/save.test.js similarity index 100% rename from test/integration/datastore/async_methods/save/index.test.js rename to test/integration/datastore/async_methods/save.test.js diff --git a/test/integration/datastore/sync_methods/changes/index.test.js b/test/integration/datastore/sync_methods/changes.test.js similarity index 100% rename from test/integration/datastore/sync_methods/changes/index.test.js rename to test/integration/datastore/sync_methods/changes.test.js diff --git a/test/integration/datastore/sync_methods/defineResource/index.test.js b/test/integration/datastore/sync_methods/defineResource.test.js similarity index 100% rename from test/integration/datastore/sync_methods/defineResource/index.test.js rename to test/integration/datastore/sync_methods/defineResource.test.js diff --git a/test/integration/datastore/sync_methods/eject/index.test.js b/test/integration/datastore/sync_methods/eject.test.js similarity index 100% rename from test/integration/datastore/sync_methods/eject/index.test.js rename to test/integration/datastore/sync_methods/eject.test.js diff --git a/test/integration/datastore/sync_methods/ejectAll/index.test.js b/test/integration/datastore/sync_methods/ejectAll.test.js similarity index 100% rename from test/integration/datastore/sync_methods/ejectAll/index.test.js rename to test/integration/datastore/sync_methods/ejectAll.test.js diff --git a/test/integration/datastore/sync_methods/filter/index.test.js b/test/integration/datastore/sync_methods/filter.test.js similarity index 100% rename from test/integration/datastore/sync_methods/filter/index.test.js rename to test/integration/datastore/sync_methods/filter.test.js diff --git a/test/integration/datastore/sync_methods/get/index.test.js b/test/integration/datastore/sync_methods/get.test.js similarity index 100% rename from test/integration/datastore/sync_methods/get/index.test.js rename to test/integration/datastore/sync_methods/get.test.js diff --git a/test/integration/datastore/sync_methods/hasChanges/index.test.js b/test/integration/datastore/sync_methods/hasChanges.test.js similarity index 100% rename from test/integration/datastore/sync_methods/hasChanges/index.test.js rename to test/integration/datastore/sync_methods/hasChanges.test.js diff --git a/test/integration/datastore/sync_methods/inject/index.test.js b/test/integration/datastore/sync_methods/inject.test.js similarity index 100% rename from test/integration/datastore/sync_methods/inject/index.test.js rename to test/integration/datastore/sync_methods/inject.test.js diff --git a/test/integration/datastore/sync_methods/lastModified/index.test.js b/test/integration/datastore/sync_methods/lastModified.test.js similarity index 100% rename from test/integration/datastore/sync_methods/lastModified/index.test.js rename to test/integration/datastore/sync_methods/lastModified.test.js diff --git a/test/integration/datastore/sync_methods/lastSaved/index.test.js b/test/integration/datastore/sync_methods/lastSaved.test.js similarity index 100% rename from test/integration/datastore/sync_methods/lastSaved/index.test.js rename to test/integration/datastore/sync_methods/lastSaved.test.js diff --git a/test/integration/datastore/sync_methods/previous/index.test.js b/test/integration/datastore/sync_methods/previous.test.js similarity index 100% rename from test/integration/datastore/sync_methods/previous/index.test.js rename to test/integration/datastore/sync_methods/previous.test.js From 976a22b1c19fb26ccf0834b428850d784f6184f6 Mon Sep 17 00:00:00 2001 From: Jason Dobry Date: Wed, 14 May 2014 23:33:16 -0600 Subject: [PATCH 2/3] Closes #49. Closes #50. --- CHANGELOG.md | 2 + README.md | 3 +- dist/angular-data.js | 332 +++++++++++++++--- dist/angular-data.min.js | 4 +- karma.start.js | 16 +- src/datastore/sync_methods/bindAll.js | 70 ++++ src/datastore/sync_methods/bindOne.js | 58 +++ src/datastore/sync_methods/index.js | 20 ++ src/index.js | 6 +- src/utils.js | 1 + .../datastore/sync_methods/bindAll.test.js | 64 ++++ .../datastore/sync_methods/bindOne.test.js | 63 ++++ 12 files changed, 567 insertions(+), 72 deletions(-) create mode 100644 src/datastore/sync_methods/bindAll.js create mode 100644 src/datastore/sync_methods/bindOne.js create mode 100644 test/integration/datastore/sync_methods/bindAll.test.js create mode 100644 test/integration/datastore/sync_methods/bindOne.test.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b3f03f..2e80c7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ###### Backwards compatible API changes - #30, #48 - DSCacheFactory integration +- #49 - DS.bindOne($scope, prop, resourceName, id) +- #50 - DS.bindAll($scope, prop, resourceName, query) ##### 0.8.1 - 02 May 2014 diff --git a/README.md b/README.md index 8d92bf2..27730da 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ __Data store for Angular.js.__ -__Current version:__ 0.8.0 +__Current version:__ 0.9.0 Angular-data is in a pre-1.0.0 development stage; the API is fluctuating, not a lot of tests yet, etc. @@ -13,7 +13,6 @@ Pending: - Relations/Associations - Various Adapters - Schema Definition/Validation -- Cache Expiry - Nested Resources - Website / documentation - 100% tested (whatever _that_ means) diff --git a/dist/angular-data.js b/dist/angular-data.js index 0c92f4c..22cbad4 100644 --- a/dist/angular-data.js +++ b/dist/angular-data.js @@ -504,7 +504,7 @@ var indexOf = require('./indexOf'); module.exports = contains; -},{"./indexOf":5}],4:[function(require,module,exports){ +},{"./indexOf":6}],4:[function(require,module,exports){ var makeIterator = require('../function/makeIterator_'); /** @@ -532,7 +532,32 @@ var makeIterator = require('../function/makeIterator_'); -},{"../function/makeIterator_":11}],5:[function(require,module,exports){ +},{"../function/makeIterator_":12}],5:[function(require,module,exports){ + + + /** + * Array forEach + */ + function forEach(arr, callback, thisObj) { + if (arr == null) { + return; + } + var i = -1, + len = arr.length; + while (++i < len) { + // we iterate over sparse items since there is no way to make it + // work properly on IE 7-8. see #64 + if ( callback.call(thisObj, arr[i], i, arr) === false ) { + break; + } + } + } + + module.exports = forEach; + + + +},{}],6:[function(require,module,exports){ /** @@ -562,7 +587,7 @@ var makeIterator = require('../function/makeIterator_'); module.exports = indexOf; -},{}],6:[function(require,module,exports){ +},{}],7:[function(require,module,exports){ var filter = require('./filter'); function isValidString(val) { @@ -581,7 +606,7 @@ var filter = require('./filter'); module.exports = join; -},{"./filter":4}],7:[function(require,module,exports){ +},{"./filter":4}],8:[function(require,module,exports){ /** @@ -618,7 +643,7 @@ var filter = require('./filter'); -},{}],8:[function(require,module,exports){ +},{}],9:[function(require,module,exports){ /** @@ -675,7 +700,7 @@ var filter = require('./filter'); -},{}],9:[function(require,module,exports){ +},{}],10:[function(require,module,exports){ var isFunction = require('../lang/isFunction'); /** @@ -705,7 +730,7 @@ var isFunction = require('../lang/isFunction'); module.exports = toLookup; -},{"../lang/isFunction":15}],10:[function(require,module,exports){ +},{"../lang/isFunction":16}],11:[function(require,module,exports){ /** @@ -719,7 +744,7 @@ var isFunction = require('../lang/isFunction'); -},{}],11:[function(require,module,exports){ +},{}],12:[function(require,module,exports){ var identity = require('./identity'); var prop = require('./prop'); var deepMatches = require('../object/deepMatches'); @@ -755,7 +780,7 @@ var deepMatches = require('../object/deepMatches'); -},{"../object/deepMatches":20,"./identity":10,"./prop":12}],12:[function(require,module,exports){ +},{"../object/deepMatches":21,"./identity":11,"./prop":13}],13:[function(require,module,exports){ /** @@ -771,7 +796,7 @@ var deepMatches = require('../object/deepMatches'); -},{}],13:[function(require,module,exports){ +},{}],14:[function(require,module,exports){ var isKind = require('./isKind'); /** */ @@ -781,7 +806,7 @@ var isKind = require('./isKind'); module.exports = isArray; -},{"./isKind":16}],14:[function(require,module,exports){ +},{"./isKind":17}],15:[function(require,module,exports){ var forOwn = require('../object/forOwn'); var isArray = require('./isArray'); @@ -807,7 +832,7 @@ var isArray = require('./isArray'); -},{"../object/forOwn":23,"./isArray":13}],15:[function(require,module,exports){ +},{"../object/forOwn":24,"./isArray":14}],16:[function(require,module,exports){ var isKind = require('./isKind'); /** */ @@ -817,7 +842,7 @@ var isKind = require('./isKind'); module.exports = isFunction; -},{"./isKind":16}],16:[function(require,module,exports){ +},{"./isKind":17}],17:[function(require,module,exports){ var kindOf = require('./kindOf'); /** * Check if value is from a specific "kind". @@ -828,7 +853,7 @@ var kindOf = require('./kindOf'); module.exports = isKind; -},{"./kindOf":18}],17:[function(require,module,exports){ +},{"./kindOf":19}],18:[function(require,module,exports){ /** @@ -843,7 +868,7 @@ var kindOf = require('./kindOf'); -},{}],18:[function(require,module,exports){ +},{}],19:[function(require,module,exports){ var _rKind = /^\[object (.*)\]$/, @@ -865,7 +890,7 @@ var kindOf = require('./kindOf'); module.exports = kindOf; -},{}],19:[function(require,module,exports){ +},{}],20:[function(require,module,exports){ /** @@ -880,7 +905,7 @@ var kindOf = require('./kindOf'); -},{}],20:[function(require,module,exports){ +},{}],21:[function(require,module,exports){ var forOwn = require('./forOwn'); var isArray = require('../lang/isArray'); @@ -937,7 +962,7 @@ var isArray = require('../lang/isArray'); -},{"../lang/isArray":13,"./forOwn":23}],21:[function(require,module,exports){ +},{"../lang/isArray":14,"./forOwn":24}],22:[function(require,module,exports){ var forOwn = require('./forOwn'); var isPlainObject = require('../lang/isPlainObject'); @@ -973,7 +998,7 @@ var isPlainObject = require('../lang/isPlainObject'); -},{"../lang/isPlainObject":17,"./forOwn":23}],22:[function(require,module,exports){ +},{"../lang/isPlainObject":18,"./forOwn":24}],23:[function(require,module,exports){ var hasOwn = require('./hasOwn'); var _hasDontEnumBug, @@ -1051,7 +1076,7 @@ var hasOwn = require('./hasOwn'); -},{"./hasOwn":24}],23:[function(require,module,exports){ +},{"./hasOwn":25}],24:[function(require,module,exports){ var hasOwn = require('./hasOwn'); var forIn = require('./forIn'); @@ -1072,7 +1097,7 @@ var forIn = require('./forIn'); -},{"./forIn":22,"./hasOwn":24}],24:[function(require,module,exports){ +},{"./forIn":23,"./hasOwn":25}],25:[function(require,module,exports){ /** @@ -1086,7 +1111,28 @@ var forIn = require('./forIn'); -},{}],25:[function(require,module,exports){ +},{}],26:[function(require,module,exports){ +var forEach = require('../array/forEach'); + + /** + * Create nested object if non-existent + */ + function namespace(obj, path){ + if (!path) return obj; + forEach(path.split('.'), function(key){ + if (!obj[key]) { + obj[key] = {}; + } + obj = obj[key]; + }); + return obj; + } + + module.exports = namespace; + + + +},{"../array/forEach":5}],27:[function(require,module,exports){ var slice = require('../array/slice'); /** @@ -1106,7 +1152,26 @@ var slice = require('../array/slice'); -},{"../array/slice":7}],26:[function(require,module,exports){ +},{"../array/slice":8}],28:[function(require,module,exports){ +var namespace = require('./namespace'); + + /** + * set "nested" object property + */ + function set(obj, prop, val){ + var parts = (/^(.+)\.(.+)$/).exec(prop); + if (parts){ + namespace(obj, parts[1])[parts[2]] = val; + } else { + obj[prop] = val; + } + } + + module.exports = set; + + + +},{"./namespace":26}],29:[function(require,module,exports){ var join = require('../array/join'); var slice = require('../array/slice'); @@ -1123,7 +1188,7 @@ var slice = require('../array/slice'); module.exports = makePath; -},{"../array/join":6,"../array/slice":7}],27:[function(require,module,exports){ +},{"../array/join":7,"../array/slice":8}],30:[function(require,module,exports){ var toString = require('../lang/toString'); /** * "Safer" String.toUpperCase() @@ -1135,7 +1200,7 @@ var toString = require('../lang/toString'); module.exports = upperCase; -},{"../lang/toString":19}],28:[function(require,module,exports){ +},{"../lang/toString":20}],31:[function(require,module,exports){ /** * @doc function * @id DSHttpAdapterProvider @@ -1569,7 +1634,7 @@ function DSHttpAdapterProvider() { module.exports = DSHttpAdapterProvider; -},{}],29:[function(require,module,exports){ +},{}],32:[function(require,module,exports){ var errorPrefix = 'DS.create(resourceName, attrs): '; /** @@ -1668,7 +1733,7 @@ function create(resourceName, attrs, options) { module.exports = create; -},{}],30:[function(require,module,exports){ +},{}],33:[function(require,module,exports){ var errorPrefix = 'DS.destroy(resourceName, id): '; /** @@ -1755,7 +1820,7 @@ function destroy(resourceName, id, options) { module.exports = destroy; -},{}],31:[function(require,module,exports){ +},{}],34:[function(require,module,exports){ var errorPrefix = 'DS.destroyAll(resourceName, params[, options]): '; /** @@ -1850,7 +1915,7 @@ function destroyAll(resourceName, params, options) { module.exports = destroyAll; -},{}],32:[function(require,module,exports){ +},{}],35:[function(require,module,exports){ var errorPrefix = 'DS.find(resourceName, id[, options]): '; /** @@ -1956,7 +2021,7 @@ function find(resourceName, id, options) { module.exports = find; -},{}],33:[function(require,module,exports){ +},{}],36:[function(require,module,exports){ var errorPrefix = 'DS.findAll(resourceName, params[, options]): '; function processResults(utils, data, resourceName, queryHash) { @@ -2115,7 +2180,7 @@ function findAll(resourceName, params, options) { module.exports = findAll; -},{}],34:[function(require,module,exports){ +},{}],37:[function(require,module,exports){ module.exports = { /** * @doc method @@ -2188,7 +2253,7 @@ module.exports = { save: require('./save') }; -},{"./create":29,"./destroy":30,"./destroyAll":31,"./find":32,"./findAll":33,"./refresh":35,"./save":36}],35:[function(require,module,exports){ +},{"./create":32,"./destroy":33,"./destroyAll":34,"./find":35,"./findAll":36,"./refresh":38,"./save":39}],38:[function(require,module,exports){ var errorPrefix = 'DS.refresh(resourceName, id[, options]): '; /** @@ -2262,7 +2327,7 @@ function refresh(resourceName, id, options) { module.exports = refresh; -},{}],36:[function(require,module,exports){ +},{}],39:[function(require,module,exports){ var errorPrefix = 'DS.save(resourceName, id[, options]): '; /** @@ -2381,7 +2446,7 @@ function save(resourceName, id, options) { module.exports = save; -},{}],37:[function(require,module,exports){ +},{}],40:[function(require,module,exports){ var utils = require('../utils')[0](); function lifecycleNoop(resourceName, attrs, cb) { @@ -2585,7 +2650,139 @@ function DSProvider() { module.exports = DSProvider; -},{"../utils":"iWjGJZ","./async_methods":34,"./sync_methods":46}],38:[function(require,module,exports){ +},{"../utils":"K0yknU","./async_methods":37,"./sync_methods":51}],41:[function(require,module,exports){ +var errorPrefix = 'DS.bindAll(scope, expr, resourceName, params): '; + +/** + * @doc method + * @id DS.sync_methods:bindAll + * @name bindAll + * @description + * Bind a collection of items in the data store to `scope` under the property specified by `expr` filtered by `params`. + * + * ## Signature: + * ```js + * DS.bindAll(scope, expr, resourceName, params) + * ``` + * + * ## Example: + * + * ```js + * // bind the documents with ownerId of 5 to the 'docs' property of the $scope + * var deregisterFunc = DS.bindAll($scope, 'docs', 'document', { + * query: { + * criteria: { + * ownerId: 5 + * } + * } + * }); + * ``` + * + * ## Throws + * + * - `{IllegalArgumentError}` + * - `{RuntimeError}` + * - `{UnhandledError}` + * + * @param {object} scope The scope to bind to. + * @param {string} expr An expression used to bind to the scope. Can be used to set nested keys, i.e. `"user.comments"`. + * @param {string} resourceName The resource type, e.g. 'user', 'comment', etc. + * @param {object} params Parameter object that is used in filtering the collection. Properties: + * + * - `{object=}` - `query` - The query object by which to filter items of the type specified by `resourceName`. Properties: + * - `{object=}` - `where` - Where clause. + * - `{number=}` - `limit` - Limit clause. + * - `{skip=}` - `skip` - Skip clause. + * - `{orderBy=}` - `orderBy` - OrderBy clause. + * @returns {function} Scope $watch deregistration function. + */ +function bindOne(scope, expr, resourceName, params) { + if (!this.utils.isObject(scope)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'scope: Must be an object!'); + } else if (!this.utils.isString(expr)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'expr: Must be a string!'); + } else if (!this.definitions[resourceName]) { + throw new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); + } else if (!this.utils.isObject(params)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'params: Must be an object!'); + } + + var _this = this; + + try { + return scope.$watch(function () { + return _this.lastModified(resourceName); + }, function () { + _this.utils.set(scope, expr, _this.filter(resourceName, params)); + }); + } catch (err) { + throw new this.errors.UnhandledError(err); + } +} + +module.exports = bindOne; + +},{}],42:[function(require,module,exports){ +var errorPrefix = 'DS.bindOne(scope, expr, resourceName, id): '; + +/** + * @doc method + * @id DS.sync_methods:bindOne + * @name bindOne + * @description + * Bind an item in the data store to `scope` under the property specified by `expr`. + * + * ## Signature: + * ```js + * DS.bindOne(scope, expr, resourceName, id) + * ``` + * + * ## Example: + * + * ```js + * // bind the document with id 5 to the 'doc' property of the $scope + * var deregisterFunc = DS.bindOne($scope, 'doc', 'document', 5); + * ``` + * + * ## Throws + * + * - `{IllegalArgumentError}` + * - `{RuntimeError}` + * - `{UnhandledError}` + * + * @param {object} scope The scope to bind to. + * @param {string} expr An expression used to bind to the scope. Can be used to set nested keys, i.e. `"user.profile"`. + * @param {string} resourceName The resource type, e.g. 'user', 'comment', etc. + * @param {string|number} id The primary key of the item to bind. + * @returns {function} Scope $watch deregistration function. + */ +function bindOne(scope, expr, resourceName, id) { + if (!this.utils.isObject(scope)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'scope: Must be an object!'); + } else if (!this.utils.isString(expr)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'expr: Must be a string!'); + } else if (!this.definitions[resourceName]) { + throw new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); + } else if (!this.utils.isString(id) && !this.utils.isNumber(id)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!'); + } + + var _this = this; + + try { + return scope.$watch(function () { + return _this.lastModified(resourceName, id); + }, function () { + _this.utils.set(scope, expr, _this.get(resourceName, id)); + }); + } catch (err) { + throw new this.errors.UnhandledError(err); + } +} + +module.exports = bindOne; + +},{}],43:[function(require,module,exports){ var errorPrefix = 'DS.changes(resourceName, id): '; /** @@ -2642,7 +2839,7 @@ function changes(resourceName, id) { module.exports = changes; -},{}],39:[function(require,module,exports){ +},{}],44:[function(require,module,exports){ var errorPrefix = 'DS.defineResource(definition): '; function Resource(utils, options) { @@ -2764,7 +2961,7 @@ function defineResource(definition) { module.exports = defineResource; -},{}],40:[function(require,module,exports){ +},{}],45:[function(require,module,exports){ var observe = require('observejs'); /** @@ -2806,7 +3003,7 @@ function digest() { module.exports = digest; -},{"observejs":"QYwGEY"}],41:[function(require,module,exports){ +},{"observejs":"QYwGEY"}],46:[function(require,module,exports){ var errorPrefix = 'DS.eject(resourceName, id): '; function _eject(definition, resource, id) { @@ -2889,7 +3086,7 @@ function eject(resourceName, id) { module.exports = eject; -},{}],42:[function(require,module,exports){ +},{}],47:[function(require,module,exports){ var errorPrefix = 'DS.ejectAll(resourceName[, params]): '; function _ejectAll(definition, resource, params) { @@ -2993,7 +3190,7 @@ function ejectAll(resourceName, params) { module.exports = ejectAll; -},{}],43:[function(require,module,exports){ +},{}],48:[function(require,module,exports){ /* jshint loopfunc: true */ var errorPrefix = 'DS.filter(resourceName, params[, options]): '; @@ -3150,7 +3347,7 @@ function filter(resourceName, params, options) { module.exports = filter; -},{}],44:[function(require,module,exports){ +},{}],49:[function(require,module,exports){ var errorPrefix = 'DS.get(resourceName, id[, options]): '; /** @@ -3213,7 +3410,7 @@ function get(resourceName, id, options) { module.exports = get; -},{}],45:[function(require,module,exports){ +},{}],50:[function(require,module,exports){ var errorPrefix = 'DS.hasChanges(resourceName, id): '; function diffIsEmpty(utils, diff) { @@ -3276,7 +3473,7 @@ function hasChanges(resourceName, id) { module.exports = hasChanges; -},{}],46:[function(require,module,exports){ +},{}],51:[function(require,module,exports){ module.exports = { /** * @doc method @@ -3288,6 +3485,26 @@ module.exports = { */ defineResource: require('./defineResource'), + /** + * @doc method + * @id DS.sync_methods:bindOne + * @name bindOne + * @methodOf DS + * @description + * See [DS.bindOne](/documentation/api/api/DS.sync_methods:bindOne). + */ + bindOne: require('./bindOne'), + + /** + * @doc method + * @id DS.sync_methods:bindAll + * @name bindAll + * @methodOf DS + * @description + * See [DS.bindAll](/documentation/api/api/DS.sync_methods:bindAll). + */ + bindAll: require('./bindAll'), + /** * @doc method * @id DS.sync_methods:eject @@ -3399,7 +3616,7 @@ module.exports = { hasChanges: require('./hasChanges') }; -},{"./changes":38,"./defineResource":39,"./digest":40,"./eject":41,"./ejectAll":42,"./filter":43,"./get":44,"./hasChanges":45,"./inject":47,"./lastModified":48,"./lastSaved":49,"./previous":50}],47:[function(require,module,exports){ +},{"./bindAll":41,"./bindOne":42,"./changes":43,"./defineResource":44,"./digest":45,"./eject":46,"./ejectAll":47,"./filter":48,"./get":49,"./hasChanges":50,"./inject":52,"./lastModified":53,"./lastSaved":54,"./previous":55}],52:[function(require,module,exports){ var observe = require('observejs'), errorPrefix = 'DS.inject(resourceName, attrs[, options]): '; @@ -3543,7 +3760,7 @@ function inject(resourceName, attrs, options) { module.exports = inject; -},{"observejs":"QYwGEY"}],48:[function(require,module,exports){ +},{"observejs":"QYwGEY"}],53:[function(require,module,exports){ var errorPrefix = 'DS.lastModified(resourceName[, id]): '; /** @@ -3601,7 +3818,7 @@ function lastModified(resourceName, id) { module.exports = lastModified; -},{}],49:[function(require,module,exports){ +},{}],54:[function(require,module,exports){ var errorPrefix = 'DS.lastSaved(resourceName[, id]): '; /** @@ -3662,7 +3879,7 @@ function lastSaved(resourceName, id) { module.exports = lastSaved; -},{}],50:[function(require,module,exports){ +},{}],55:[function(require,module,exports){ var errorPrefix = 'DS.previous(resourceName, id): '; /** @@ -3718,8 +3935,8 @@ function previous(resourceName, id) { module.exports = previous; },{}],"errors":[function(require,module,exports){ -module.exports=require('ht0wMj'); -},{}],"ht0wMj":[function(require,module,exports){ +module.exports=require('XIsZmp'); +},{}],"XIsZmp":[function(require,module,exports){ /** * @doc function * @id errors.types:UnhandledError @@ -3890,7 +4107,7 @@ module.exports = [function () { }; }]; -},{}],53:[function(require,module,exports){ +},{}],58:[function(require,module,exports){ (function (window, angular, undefined) { 'use strict'; @@ -3935,9 +4152,9 @@ module.exports = [function () { * [DSUtils](/documentation/api/api/DSUtils) has some useful utility methods. * [DSErrors](/documentation/api/api/DSErrors) provides references to the various errors thrown by the data store. */ - angular.module('angular-data.DS', ['ng', 'angular-data.DSCacheFactory']) - .service('DSUtils', require('./utils')) - .service('DSErrors', require('./errors')) + angular.module('angular-data.DS', ['ng']) + .factory('DSUtils', require('./utils')) + .factory('DSErrors', require('./errors')) .provider('DSHttpAdapter', require('./adapters/http')) .provider('DS', require('./datastore')) .config(['$provide', function ($provide) { @@ -3972,9 +4189,7 @@ module.exports = [function () { })(window, window.angular); -},{"./adapters/http":28,"./datastore":37,"./errors":"ht0wMj","./utils":"iWjGJZ"}],"utils":[function(require,module,exports){ -module.exports=require('iWjGJZ'); -},{}],"iWjGJZ":[function(require,module,exports){ +},{"./adapters/http":31,"./datastore":40,"./errors":"XIsZmp","./utils":"K0yknU"}],"K0yknU":[function(require,module,exports){ module.exports = [function () { return { isString: angular.isString, @@ -3989,6 +4204,7 @@ module.exports = [function () { deepMixIn: require('mout/object/deepMixIn'), forOwn: require('mout/object/forOwn'), pick: require('mout/object/pick'), + set: require('mout/object/set'), contains: require('mout/array/contains'), filter: require('mout/array/filter'), toLookup: require('mout/array/toLookup'), @@ -4055,4 +4271,6 @@ module.exports = [function () { }; }]; -},{"mout/array/contains":3,"mout/array/filter":4,"mout/array/slice":7,"mout/array/sort":8,"mout/array/toLookup":9,"mout/lang/isEmpty":14,"mout/object/deepMixIn":21,"mout/object/forOwn":23,"mout/object/pick":25,"mout/string/makePath":26,"mout/string/upperCase":27}]},{},[53]) +},{"mout/array/contains":3,"mout/array/filter":4,"mout/array/slice":8,"mout/array/sort":9,"mout/array/toLookup":10,"mout/lang/isEmpty":15,"mout/object/deepMixIn":22,"mout/object/forOwn":24,"mout/object/pick":27,"mout/object/set":28,"mout/string/makePath":29,"mout/string/upperCase":30}],"utils":[function(require,module,exports){ +module.exports=require('K0yknU'); +},{}]},{},[58]) diff --git a/dist/angular-data.min.js b/dist/angular-data.min.js index 6258101..98146fc 100644 --- a/dist/angular-data.min.js +++ b/dist/angular-data.min.js @@ -7,5 +7,5 @@ * * @overview Data store for Angular.js. */ -require=function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var j=c[g]={exports:{}};b[g][0].call(j.exports,function(a){var c=b[g][1][a];return e(c?c:a)},j,j.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;gb&&a.check();)a.report(),b++}function e(a){for(var b in a)return!1;return!0}function f(a){return e(a.added)&&e(a.removed)&&e(a.changed)}function g(a,b){var c={},d={},e={};for(var f in b){var g=a[f];(void 0===g||g!==b[f])&&(f in a?g!==b[f]&&(e[f]=g):d[f]=void 0)}for(var f in a)f in b||(c[f]=a[f]);return Array.isArray(a)&&a.length!==b.length&&(e.length=a.length),{added:c,removed:d,changed:e}}function h(a,b){var c=b||(Array.isArray(a)?[]:{});for(var d in a)c[d]=a[d];return Array.isArray(a)&&(c.length=a.length),c}function i(a,b,c,d){if(this.closed=!1,this.object=a,this.callback=b,this.target=c,this.token=d,this.reporting=!0,n){var e=this;this.boundInternalCallback=function(a){e.internalCallback(a)}}j(this),this.connect(),this.sync(!0)}function j(a){u&&(t.push(a),i._allObserversCount++)}function k(a,b,c,d){i.call(this,a,b,c,d)}function l(a){this.arr=[],this.callback=a,this.isObserved=!0}function m(a,b,c){for(var d={},e={},f=0;fa&&b.anyChanged);i._allObserversCount=t.length,v=!1}}},u&&(a.Platform.clearObservers=function(){t=[]}),k.prototype=r({__proto__:i.prototype,connect:function(){n&&Object.observe(this.object,this.boundInternalCallback)},sync:function(){n||(this.oldObject=h(this.object))},check:function(a){var b,c;if(n){if(!a)return!1;c={},b=m(this.object,a,c)}else c=this.oldObject,b=g(this.object,this.oldObject);return f(b)?!1:(this.reportArgs=[b.added||{},b.removed||{},b.changed||{}],this.reportArgs.push(function(a){return c[a]}),!0)},disconnect:function(){n?this.object&&Object.unobserve(this.object,this.boundInternalCallback):this.oldObject=void 0}});var x=Object.getPrototypeOf({}),y=Object.getPrototypeOf([]);l.prototype={reset:function(){this.isObserved=!this.isObserved},observe:function(a){if(c(a)&&a!==x&&a!==y){var b=this.arr.indexOf(a);b>=0&&this.arr[b+1]===this.isObserved||(0>b&&(b=this.arr.length,this.arr[b]=a,Object.observe(a,this.callback)),this.arr[b+1]=this.isObserved,this.observe(Object.getPrototypeOf(a)))}},cleanup:function(){for(var a=0,b=0,c=this.isObserved;ba&&(this.arr[a]=d,this.arr[a+1]=c),a+=2):Object.unobserve(d,this.callback),b+=2}this.arr.length=a}};var z={"new":!0,updated:!0,deleted:!0};a.Observer=i,a.Observer.hasObjectObserve=n,a.ObjectObserver=k}((c.Number={isNaN:window.isNaN})?c:c)}).call(this,"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],observejs:[function(a,b){b.exports=a("QYwGEY")},{}],3:[function(a,b){function c(a,b){return-1!==d(a,b)}var d=a("./indexOf");b.exports=c},{"./indexOf":5}],4:[function(a,b){function c(a,b,c){b=d(b,c);var e=[];if(null==a)return e;for(var f,g=-1,h=a.length;++gc?d+c:c;d>e;){if(a[e]===b)return e;e++}return-1}b.exports=c},{}],6:[function(a,b){function c(a){return null!=a&&""!==a}function d(a,b){return b=b||"",e(a,c).join(b)}var e=a("./filter");b.exports=d},{"./filter":4}],7:[function(a,b){function c(a,b,c){var d=a.length;b=null==b?0:0>b?Math.max(d+b,0):Math.min(b,d),c=null==c?d:0>c?Math.max(d+c,0):Math.min(c,d);for(var e=[];c>b;)e.push(a[b++]);return e}b.exports=c},{}],8:[function(a,b){function c(a,b){if(null==a)return[];if(a.length<2)return a;null==b&&(b=d);var f,g,h;return f=~~(a.length/2),g=c(a.slice(0,f),b),h=c(a.slice(f,a.length),b),e(g,h,b)}function d(a,b){return b>a?-1:a>b?1:0}function e(a,b,c){for(var d=[];a.length&&b.length;)d.push(c(a[0],b[0])<=0?a.shift():b.shift());return a.length&&d.push.apply(d,a),b.length&&d.push.apply(d,b),d}b.exports=c},{}],9:[function(a,b){function c(a,b){var c={};if(null==a)return c;var e,f=-1,g=a.length;if(d(b))for(;++f"in a?d=d&&c[b]>a[">"]:">="in a?d=d&&c[b]>=a[">="]:"<"in a?d=d&&c[b]e?-1:e>d?1:0:e>d?-1:d>e?1:0})}}return this.utils.isNumber(b.query.limit)&&this.utils.isNumber(b.query.skip)?i=this.utils.slice(i,b.query.skip,Math.min(i.length,b.query.skip+b.query.limit)):this.utils.isNumber(b.query.limit)?i=this.utils.slice(i,0,Math.min(i.length,b.query.limit)):this.utils.isNumber(b.query.skip)&&(i=b.query.skip=b?a+1:b},deepFreeze:function b(a){if("function"==typeof Object.freeze){var c,d;Object.freeze(a);for(d in a)c=a[d],a.hasOwnProperty(d)&&"object"==typeof c&&!Object.isFrozen(c)&&b(c)}},diffObjectFromOldObject:function(a,b){var c={},d={},e={};for(var f in b){var g=a[f];(void 0===g||g!==b[f])&&(f in a?g!==b[f]&&(e[f]=g):d[f]=void 0)}for(var h in a)h in b||(c[h]=a[h]);return{added:c,removed:d,changed:e}}}}]},{"mout/array/contains":3,"mout/array/filter":4,"mout/array/slice":7,"mout/array/sort":8,"mout/array/toLookup":9,"mout/lang/isEmpty":14,"mout/object/deepMixIn":21,"mout/object/forOwn":23,"mout/object/pick":25,"mout/string/makePath":26,"mout/string/upperCase":27}]},{},[53]); \ No newline at end of file +require=function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var j=c[g]={exports:{}};b[g][0].call(j.exports,function(a){var c=b[g][1][a];return e(c?c:a)},j,j.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;gb&&a.check();)a.report(),b++}function e(a){for(var b in a)return!1;return!0}function f(a){return e(a.added)&&e(a.removed)&&e(a.changed)}function g(a,b){var c={},d={},e={};for(var f in b){var g=a[f];(void 0===g||g!==b[f])&&(f in a?g!==b[f]&&(e[f]=g):d[f]=void 0)}for(var f in a)f in b||(c[f]=a[f]);return Array.isArray(a)&&a.length!==b.length&&(e.length=a.length),{added:c,removed:d,changed:e}}function h(a,b){var c=b||(Array.isArray(a)?[]:{});for(var d in a)c[d]=a[d];return Array.isArray(a)&&(c.length=a.length),c}function i(a,b,c,d){if(this.closed=!1,this.object=a,this.callback=b,this.target=c,this.token=d,this.reporting=!0,n){var e=this;this.boundInternalCallback=function(a){e.internalCallback(a)}}j(this),this.connect(),this.sync(!0)}function j(a){u&&(t.push(a),i._allObserversCount++)}function k(a,b,c,d){i.call(this,a,b,c,d)}function l(a){this.arr=[],this.callback=a,this.isObserved=!0}function m(a,b,c){for(var d={},e={},f=0;fa&&b.anyChanged);i._allObserversCount=t.length,v=!1}}},u&&(a.Platform.clearObservers=function(){t=[]}),k.prototype=r({__proto__:i.prototype,connect:function(){n&&Object.observe(this.object,this.boundInternalCallback)},sync:function(){n||(this.oldObject=h(this.object))},check:function(a){var b,c;if(n){if(!a)return!1;c={},b=m(this.object,a,c)}else c=this.oldObject,b=g(this.object,this.oldObject);return f(b)?!1:(this.reportArgs=[b.added||{},b.removed||{},b.changed||{}],this.reportArgs.push(function(a){return c[a]}),!0)},disconnect:function(){n?this.object&&Object.unobserve(this.object,this.boundInternalCallback):this.oldObject=void 0}});var x=Object.getPrototypeOf({}),y=Object.getPrototypeOf([]);l.prototype={reset:function(){this.isObserved=!this.isObserved},observe:function(a){if(c(a)&&a!==x&&a!==y){var b=this.arr.indexOf(a);b>=0&&this.arr[b+1]===this.isObserved||(0>b&&(b=this.arr.length,this.arr[b]=a,Object.observe(a,this.callback)),this.arr[b+1]=this.isObserved,this.observe(Object.getPrototypeOf(a)))}},cleanup:function(){for(var a=0,b=0,c=this.isObserved;ba&&(this.arr[a]=d,this.arr[a+1]=c),a+=2):Object.unobserve(d,this.callback),b+=2}this.arr.length=a}};var z={"new":!0,updated:!0,deleted:!0};a.Observer=i,a.Observer.hasObjectObserve=n,a.ObjectObserver=k}((c.Number={isNaN:window.isNaN})?c:c)}).call(this,"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],observejs:[function(a,b){b.exports=a("QYwGEY")},{}],3:[function(a,b){function c(a,b){return-1!==d(a,b)}var d=a("./indexOf");b.exports=c},{"./indexOf":6}],4:[function(a,b){function c(a,b,c){b=d(b,c);var e=[];if(null==a)return e;for(var f,g=-1,h=a.length;++gc?d+c:c;d>e;){if(a[e]===b)return e;e++}return-1}b.exports=c},{}],7:[function(a,b){function c(a){return null!=a&&""!==a}function d(a,b){return b=b||"",e(a,c).join(b)}var e=a("./filter");b.exports=d},{"./filter":4}],8:[function(a,b){function c(a,b,c){var d=a.length;b=null==b?0:0>b?Math.max(d+b,0):Math.min(b,d),c=null==c?d:0>c?Math.max(d+c,0):Math.min(c,d);for(var e=[];c>b;)e.push(a[b++]);return e}b.exports=c},{}],9:[function(a,b){function c(a,b){if(null==a)return[];if(a.length<2)return a;null==b&&(b=d);var f,g,h;return f=~~(a.length/2),g=c(a.slice(0,f),b),h=c(a.slice(f,a.length),b),e(g,h,b)}function d(a,b){return b>a?-1:a>b?1:0}function e(a,b,c){for(var d=[];a.length&&b.length;)d.push(c(a[0],b[0])<=0?a.shift():b.shift());return a.length&&d.push.apply(d,a),b.length&&d.push.apply(d,b),d}b.exports=c},{}],10:[function(a,b){function c(a,b){var c={};if(null==a)return c;var e,f=-1,g=a.length;if(d(b))for(;++f"in a?d=d&&c[b]>a[">"]:">="in a?d=d&&c[b]>=a[">="]:"<"in a?d=d&&c[b]e?-1:e>d?1:0:e>d?-1:d>e?1:0})}}return this.utils.isNumber(b.query.limit)&&this.utils.isNumber(b.query.skip)?i=this.utils.slice(i,b.query.skip,Math.min(i.length,b.query.skip+b.query.limit)):this.utils.isNumber(b.query.limit)?i=this.utils.slice(i,0,Math.min(i.length,b.query.limit)):this.utils.isNumber(b.query.skip)&&(i=b.query.skip=b?a+1:b},deepFreeze:function b(a){if("function"==typeof Object.freeze){var c,d;Object.freeze(a);for(d in a)c=a[d],a.hasOwnProperty(d)&&"object"==typeof c&&!Object.isFrozen(c)&&b(c)}},diffObjectFromOldObject:function(a,b){var c={},d={},e={};for(var f in b){var g=a[f];(void 0===g||g!==b[f])&&(f in a?g!==b[f]&&(e[f]=g):d[f]=void 0)}for(var h in a)h in b||(c[h]=a[h]);return{added:c,removed:d,changed:e}}}}]},{"mout/array/contains":3,"mout/array/filter":4,"mout/array/slice":8,"mout/array/sort":9,"mout/array/toLookup":10,"mout/lang/isEmpty":15,"mout/object/deepMixIn":22,"mout/object/forOwn":24,"mout/object/pick":27,"mout/object/set":28,"mout/string/makePath":29,"mout/string/upperCase":30}],utils:[function(a,b){b.exports=a("K0yknU")},{}]},{},[58]); \ No newline at end of file diff --git a/karma.start.js b/karma.start.js index 04a7dca..88521f5 100644 --- a/karma.start.js +++ b/karma.start.js @@ -27,7 +27,7 @@ var fail = function (msg) { }], TYPES_EXCEPT_FUNCTION = ['string', 123, 123.123, null, undefined, {}, [], true, false]; -angular.module('app', ['ng', 'angular-data.DS']); +angular.module('app', ['ng', 'angular-data.DS', 'angular-data.DSCacheFactory']); // Setup before each test beforeEach(function (done) { @@ -101,15 +101,15 @@ beforeEach(function (done) { lifecycle.afterUpdate.callCount = 0; lifecycle.beforeDestroy.callCount = 0; lifecycle.afterDestroy.callCount = 0; - }); - p1 = { author: 'John', age: 30, id: 5 }; - p2 = { author: 'Sally', age: 31, id: 6 }; - p3 = { author: 'Mike', age: 32, id: 7 }; - p4 = { author: 'Adam', age: 33, id: 8 }; - p5 = { author: 'Adam', age: 33, id: 9 }; + p1 = { author: 'John', age: 30, id: 5 }; + p2 = { author: 'Sally', age: 31, id: 6 }; + p3 = { author: 'Mike', age: 32, id: 7 }; + p4 = { author: 'Adam', age: 33, id: 8 }; + p5 = { author: 'Adam', age: 33, id: 9 }; - done(); + done(); + }); }); // Clean up after each test diff --git a/src/datastore/sync_methods/bindAll.js b/src/datastore/sync_methods/bindAll.js new file mode 100644 index 0000000..045275b --- /dev/null +++ b/src/datastore/sync_methods/bindAll.js @@ -0,0 +1,70 @@ +var errorPrefix = 'DS.bindAll(scope, expr, resourceName, params): '; + +/** + * @doc method + * @id DS.sync_methods:bindAll + * @name bindAll + * @description + * Bind a collection of items in the data store to `scope` under the property specified by `expr` filtered by `params`. + * + * ## Signature: + * ```js + * DS.bindAll(scope, expr, resourceName, params) + * ``` + * + * ## Example: + * + * ```js + * // bind the documents with ownerId of 5 to the 'docs' property of the $scope + * var deregisterFunc = DS.bindAll($scope, 'docs', 'document', { + * query: { + * criteria: { + * ownerId: 5 + * } + * } + * }); + * ``` + * + * ## Throws + * + * - `{IllegalArgumentError}` + * - `{RuntimeError}` + * - `{UnhandledError}` + * + * @param {object} scope The scope to bind to. + * @param {string} expr An expression used to bind to the scope. Can be used to set nested keys, i.e. `"user.comments"`. + * @param {string} resourceName The resource type, e.g. 'user', 'comment', etc. + * @param {object} params Parameter object that is used in filtering the collection. Properties: + * + * - `{object=}` - `query` - The query object by which to filter items of the type specified by `resourceName`. Properties: + * - `{object=}` - `where` - Where clause. + * - `{number=}` - `limit` - Limit clause. + * - `{skip=}` - `skip` - Skip clause. + * - `{orderBy=}` - `orderBy` - OrderBy clause. + * @returns {function} Scope $watch deregistration function. + */ +function bindOne(scope, expr, resourceName, params) { + if (!this.utils.isObject(scope)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'scope: Must be an object!'); + } else if (!this.utils.isString(expr)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'expr: Must be a string!'); + } else if (!this.definitions[resourceName]) { + throw new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); + } else if (!this.utils.isObject(params)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'params: Must be an object!'); + } + + var _this = this; + + try { + return scope.$watch(function () { + return _this.lastModified(resourceName); + }, function () { + _this.utils.set(scope, expr, _this.filter(resourceName, params)); + }); + } catch (err) { + throw new this.errors.UnhandledError(err); + } +} + +module.exports = bindOne; diff --git a/src/datastore/sync_methods/bindOne.js b/src/datastore/sync_methods/bindOne.js new file mode 100644 index 0000000..83fcd96 --- /dev/null +++ b/src/datastore/sync_methods/bindOne.js @@ -0,0 +1,58 @@ +var errorPrefix = 'DS.bindOne(scope, expr, resourceName, id): '; + +/** + * @doc method + * @id DS.sync_methods:bindOne + * @name bindOne + * @description + * Bind an item in the data store to `scope` under the property specified by `expr`. + * + * ## Signature: + * ```js + * DS.bindOne(scope, expr, resourceName, id) + * ``` + * + * ## Example: + * + * ```js + * // bind the document with id 5 to the 'doc' property of the $scope + * var deregisterFunc = DS.bindOne($scope, 'doc', 'document', 5); + * ``` + * + * ## Throws + * + * - `{IllegalArgumentError}` + * - `{RuntimeError}` + * - `{UnhandledError}` + * + * @param {object} scope The scope to bind to. + * @param {string} expr An expression used to bind to the scope. Can be used to set nested keys, i.e. `"user.profile"`. + * @param {string} resourceName The resource type, e.g. 'user', 'comment', etc. + * @param {string|number} id The primary key of the item to bind. + * @returns {function} Scope $watch deregistration function. + */ +function bindOne(scope, expr, resourceName, id) { + if (!this.utils.isObject(scope)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'scope: Must be an object!'); + } else if (!this.utils.isString(expr)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'expr: Must be a string!'); + } else if (!this.definitions[resourceName]) { + throw new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); + } else if (!this.utils.isString(id) && !this.utils.isNumber(id)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!'); + } + + var _this = this; + + try { + return scope.$watch(function () { + return _this.lastModified(resourceName, id); + }, function () { + _this.utils.set(scope, expr, _this.get(resourceName, id)); + }); + } catch (err) { + throw new this.errors.UnhandledError(err); + } +} + +module.exports = bindOne; diff --git a/src/datastore/sync_methods/index.js b/src/datastore/sync_methods/index.js index 1e342b5..e88dd4d 100644 --- a/src/datastore/sync_methods/index.js +++ b/src/datastore/sync_methods/index.js @@ -9,6 +9,26 @@ module.exports = { */ defineResource: require('./defineResource'), + /** + * @doc method + * @id DS.sync_methods:bindOne + * @name bindOne + * @methodOf DS + * @description + * See [DS.bindOne](/documentation/api/api/DS.sync_methods:bindOne). + */ + bindOne: require('./bindOne'), + + /** + * @doc method + * @id DS.sync_methods:bindAll + * @name bindAll + * @methodOf DS + * @description + * See [DS.bindAll](/documentation/api/api/DS.sync_methods:bindAll). + */ + bindAll: require('./bindAll'), + /** * @doc method * @id DS.sync_methods:eject diff --git a/src/index.js b/src/index.js index 7d5ef02..c24b0e5 100644 --- a/src/index.js +++ b/src/index.js @@ -42,9 +42,9 @@ * [DSUtils](/documentation/api/api/DSUtils) has some useful utility methods. * [DSErrors](/documentation/api/api/DSErrors) provides references to the various errors thrown by the data store. */ - angular.module('angular-data.DS', ['ng', 'angular-data.DSCacheFactory']) - .service('DSUtils', require('./utils')) - .service('DSErrors', require('./errors')) + angular.module('angular-data.DS', ['ng']) + .factory('DSUtils', require('./utils')) + .factory('DSErrors', require('./errors')) .provider('DSHttpAdapter', require('./adapters/http')) .provider('DS', require('./datastore')) .config(['$provide', function ($provide) { diff --git a/src/utils.js b/src/utils.js index 5a8ddd4..27de510 100644 --- a/src/utils.js +++ b/src/utils.js @@ -12,6 +12,7 @@ module.exports = [function () { deepMixIn: require('mout/object/deepMixIn'), forOwn: require('mout/object/forOwn'), pick: require('mout/object/pick'), + set: require('mout/object/set'), contains: require('mout/array/contains'), filter: require('mout/array/filter'), toLookup: require('mout/array/toLookup'), diff --git a/test/integration/datastore/sync_methods/bindAll.test.js b/test/integration/datastore/sync_methods/bindAll.test.js new file mode 100644 index 0000000..3e6316c --- /dev/null +++ b/test/integration/datastore/sync_methods/bindAll.test.js @@ -0,0 +1,64 @@ +describe('DS.bindAll(scope, expr, resourceName, params)', function () { + var errorPrefix = 'DS.bindAll(scope, expr, resourceName, params): '; + + var $rootScope, $scope; + + beforeEach(function () { + inject(function (_$rootScope_) { + $rootScope = _$rootScope_; + $scope = $rootScope.$new(); + }); + }); + + it('should throw an error when method pre-conditions are not met', function () { + angular.forEach(TYPES_EXCEPT_OBJECT, function (key) { + assert.throws(function () { + DS.bindAll(key); + }, DS.errors.IllegalArgumentError, errorPrefix + 'scope: Must be an object!'); + }); + + angular.forEach(TYPES_EXCEPT_STRING, function (key) { + assert.throws(function () { + DS.bindAll($scope, key); + }, DS.errors.IllegalArgumentError, errorPrefix + 'expr: Must be a string!'); + }); + + assert.throws(function () { + DS.bindAll($scope, 'post', 'does not exist', {}); + }, DS.errors.RuntimeError, errorPrefix + 'does not exist is not a registered resource!'); + + angular.forEach(TYPES_EXCEPT_OBJECT, function (key) { + assert.throws(function () { + DS.bindAll($scope, 'post', 'post', key); + }, DS.errors.IllegalArgumentError, errorPrefix + 'params: Must be an object!'); + }); + }); + it('should bind an item in the data store to the scope', function () { + + DS.inject('post', p1); + DS.inject('post', p2); + DS.inject('post', p3); + DS.inject('post', p4); + DS.inject('post', p5); + + DS.bindAll($scope, 'posts', 'post', { + query: { + where: { + age: { + '>': 31 + } + } + } + }); + + $rootScope.$apply(); + + assert.deepEqual($scope.posts, [p3, p4, p5]); + + DS.eject('post', 8); + + $rootScope.$apply(); + + assert.deepEqual($scope.posts, [p3, p5]); + }); +}); diff --git a/test/integration/datastore/sync_methods/bindOne.test.js b/test/integration/datastore/sync_methods/bindOne.test.js new file mode 100644 index 0000000..a965c8a --- /dev/null +++ b/test/integration/datastore/sync_methods/bindOne.test.js @@ -0,0 +1,63 @@ +describe('DS.bindOne(scope, expr, resourceName, id)', function () { + var errorPrefix = 'DS.bindOne(scope, expr, resourceName, id): '; + + var $rootScope, $scope; + + beforeEach(function () { + inject(function (_$rootScope_) { + $rootScope = _$rootScope_; + $scope = $rootScope.$new(); + }); + }); + + it('should throw an error when method pre-conditions are not met', function () { + angular.forEach(TYPES_EXCEPT_OBJECT, function (key) { + assert.throws(function () { + DS.bindOne(key); + }, DS.errors.IllegalArgumentError, errorPrefix + 'scope: Must be an object!'); + }); + + angular.forEach(TYPES_EXCEPT_STRING, function (key) { + assert.throws(function () { + DS.bindOne($scope, key); + }, DS.errors.IllegalArgumentError, errorPrefix + 'expr: Must be a string!'); + }); + + assert.throws(function () { + DS.bindOne($scope, 'post', 'does not exist', {}); + }, DS.errors.RuntimeError, errorPrefix + 'does not exist is not a registered resource!'); + + angular.forEach(TYPES_EXCEPT_STRING_OR_NUMBER, function (key) { + assert.throws(function () { + DS.bindOne($scope, 'post', 'post', key); + }, DS.errors.IllegalArgumentError, errorPrefix + 'id: Must be a string or a number!'); + }); + }); + it('should bind an item in the data store to the scope', function () { + + DS.inject('post', p1); + DS.inject('post', p2); + + var post = DS.get('post', 5), + post2 = DS.get('post', 6); + + DS.bindOne($scope, 'post', 'post', 5); + DS.bindOne($scope, 'other.post', 'post', 6); + DS.bindOne($scope, 'post3', 'post', 7); + + $rootScope.$apply(); + + assert.deepEqual($scope.post, post); + assert.deepEqual($scope.other.post, post2); + assert.isUndefined($scope.post2); + + post.author = 'Jason'; + + $rootScope.$apply(); + + assert.equal($scope.post.author, 'Jason'); + assert.deepEqual($scope.post, post); + assert.deepEqual($scope.other.post, post2); + assert.isUndefined($scope.post2); + }); +}); From 6bd3d3d0ac2af65bdbd16d1c77a70e40ca6a94dd Mon Sep 17 00:00:00 2001 From: Jason Dobry Date: Wed, 14 May 2014 23:39:14 -0600 Subject: [PATCH 3/3] Upgrade grunt-browserify. --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 5013263..a0b9e61 100644 --- a/package.json +++ b/package.json @@ -42,8 +42,8 @@ "karma-mocha": "~0.1.3", "karma-sinon": "~1.0.3", "time-grunt": "~0.3.1", - "browserify": "^3.46.1", - "grunt-browserify": "^2.0.8" + "browserify": "4.1.3", + "grunt-browserify": "2.1.0" }, "scripts": { "test": "node node_modules/grunt-cli/bin/grunt test"