0

Has anyone worked with the nodejs modules 'deferred' and 'mongoose'? I'm trying to get the deferred.promisify method to work on mongoose models' functions so I can chain them easily, but running into some troubles. Specifically, I'd like to promisify the find and findById functions so I can chain finding one document referenced by another document by ObjectID.

Here's what I've got: https://gist.github.com/3321827

However, this seems less than ideal since the getAppPermissions and getApplication functions seem to be little more than wrappers for the find and findById methods of the mongoose model.

I tried just passing the functions to promisify, but I get an error about Object #<Object> has no method '_applyNamedScope' which appears to be caused because this is no longer bound to the correct object. Perhaps I need to use underscore.bind? Has anyone had any success in this area, or should I just stick with what's working now?

Michael Pratt
  • 3,438
  • 1
  • 17
  • 31

2 Answers2

2

You probably didn't pass context to methods properly, right way to do this is to provide promisified versions of methods directly on Mongoose prototype:

// I assume that methods you're using are set on mongoose.Model,
// but be sure to check, maybe, they're using some other prototype (!)
var promisify = require('deferred').promisify;
var modelProto = mongoose.Model.prototype;
modelProto.pFind = promisify(modelProto.find);
modelProto.pFindById = promisify(modelProto.findById);

// After that you may use promisified methods directly:
app.get('/apps', requireLogin, function (req, res) {    
  AppPermissions.pFind({ user: req.user.id, valid: true })
    .map(function (permission) {
      return ApplicationRecord.pFindById(permission.application)(
        function (application) {
          application.permission = permisson;
          return application;
        }
      );
    }).end(function (applications) {
      res.render('applist', { applications: applications });
    }, null);
});

You can also refrain from polluting the prototype, and use methods indirectly:

var promisify = require('deferred').promisify;
var modelProto = mongoose.Model.prototype;
var pFind = promisify(modelProto.find);
var pFindById = promisify(modelProto.findById);

app.get('/apps', requireLogin, function (req, res) {    
  pFind.call(AppPermissions, { user: req.user.id, valid: true })
    .map(function (permission) {
      return pFindById.call(ApplicationRecord, permission.application)(
        function (application) {
          application.permission = permisson;
          return application;
        }
      );
    }).end(function (applications) {
      res.render('applist', { applications: applications });
    }, null);
});
Mariusz Nowak
  • 32,050
  • 5
  • 35
  • 37
  • Thanks - this actually isn't the answer (see my own answer) but it definitely helped set me down the right direction! – Michael Pratt Aug 18 '12 at 05:36
  • Your answer is really same, I just wasn't sure how to get to *right* prototype object (didn't try to run the code). Anyway, great it's solved for you :) – Mariusz Nowak Aug 18 '12 at 11:26
2

Mariusz's answer was pretty close. Here's what ended up working for me in this particular case, hopefully others can learn from this:

// I put this in my model file so I didn't have to worry about repeating it
var userProto = mongoose.model('User');

userProto.pFind = deferred.promisify(userProto.find);
userProto.pFindOne = deferred.promisify(userProto.findOne);
Michael Pratt
  • 3,438
  • 1
  • 17
  • 31