Code coverage report for bookshelf/lib/base/eager.js

Statements: 100% (49 / 49)      Branches: 100% (14 / 14)      Functions: 100% (5 / 5)      Lines: 100% (47 / 47)      Ignored: none     

All files » bookshelf/lib/base/ » eager.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112                    1 1   1 174 174 174     1         174 174 174 174 174     174       174 198 198     198 39 39 39 39       198   195 6     189   189       168         168 168 186                 168           174 174 174 201 201 159   42     174           195 195 195 195 396   195         1   1
// Eager Base
// ---------------
 
// The EagerBase provides a scaffold for handling with eager relation
// pairing, by queueing the appropriate related method calls with
// a database specific `eagerFetch` method, which then may utilize
// `pushModels` for pairing the models depending on the database need.
 
'use strict';
 
var _ = require('lodash');
var Promise = require('./promise');
 
function EagerBase(parent, parentResponse, target) {
  this.parent = parent;
  this.parentResponse = parentResponse;
  this.target = target;
}
 
_.extend(EagerBase.prototype, {
 
  // This helper function is used internally to determine which relations
  // are necessary for fetching based on the `model.load` or `withRelated` option.
  fetch: Promise.method(function (options) {
    var relationName, related;
    var target = this.target;
    var handled = this.handled = {};
    var withRelated = this.prepWithRelated(options.withRelated);
    var subRelated = {};
 
    // Internal flag to determine whether to set the ctor(s) on the `Relation` object.
    target._isEager = true;
 
    // Eager load each of the `withRelated` relation item, splitting on '.'
    // which indicates a nested eager load.
    for (var key in withRelated) {
      related = key.split('.');
      relationName = related[0];
 
      // Add additional eager items to an array, to load at the next level in the query.
      if (related.length > 1) {
        var relatedObj = {};
        subRelated[relationName] = subRelated[relationName] || [];
        relatedObj[related.slice(1).join('.')] = withRelated[key];
        subRelated[relationName].push(relatedObj);
      }
 
      // Only allow one of a certain nested type per-level.
      if (handled[relationName]) continue;
 
      if (!_.isFunction(target[relationName])) {
        throw new Error(relationName + ' is not defined on the model.');
      }
 
      var relation = target[relationName]();
 
      handled[relationName] = relation;
    }
 
    // Delete the internal flag from the model.
    delete target._isEager;
 
    // Fetch all eager loaded models, loading them onto
    // an array of pending deferred objects, which will handle
    // all necessary pairing with parent objects, etc.
    var pendingDeferred = [];
    for (relationName in handled) {
      pendingDeferred.push(this.eagerFetch(relationName, handled[relationName], _.extend({}, options, {
        isEager: true,
        withRelated: subRelated[relationName],
        _beforeFn: withRelated[relationName] || noop
      })));
    }
 
    // Return a deferred handler for all of the nested object sync
    // returning the original response when these syncs & pairings are complete.
    return Promise.all(pendingDeferred)['return'](this.parentResponse);
  }),
 
  // Prep the `withRelated` object, to normalize into an object where each
  // has a function that is called when running the query.
  prepWithRelated: function prepWithRelated(withRelated) {
    if (!_.isArray(withRelated)) withRelated = [withRelated];
    var obj = {};
    for (var i = 0, l = withRelated.length; i < l; i++) {
      var related = withRelated[i];
      if (_.isString(related)) {
        obj[related] = noop;
      } else {
        _.extend(obj, related);
      }
    }
    return obj;
  },
 
  // Pushes each of the incoming models onto a new `related` array,
  // which is used to correcly pair additional nested relations.
  pushModels: function pushModels(relationName, handled, resp) {
    var models = this.parent;
    var relatedData = handled.relatedData;
    var related = [];
    for (var i = 0, l = resp.length; i < l; i++) {
      related.push(relatedData.createModel(resp[i]));
    }
    return relatedData.eagerPair(relationName, related, models);
  }
 
});
 
var noop = function noop() {};
 
module.exports = EagerBase;