2

I am using Parse.com javascript for CloudCode. I have little interest in becoming an expert in javascript, but I need to do some. I have worked out a scheme for modularizing (dividing into separate files) but it seems like there must be a better way. In my main.js, I have code like this. The only function of main.js is to call and link the various modules together.

var mods = {};

mods.functions = require('cloud/functions.js');
mods.user = require('cloud/user.js');

mods.functions.setMods(mods);

The mods variable collects references to each module. Then for each module that needs to call other modules, I call "setMods" in that module and pass mods to it. In setMods the module gets references to any other module it wants to access.

Each module then has code like this.

exports.setMods = function (mods) {
    userMod =     mods.user;    
    constants =   mods.constants;    
};

Parse.Cloud.define("getRecoveryQuestion", function(request, response) 
{
    var p0 = userMod.lookupUserByEmail(request.params.email);
    var p1 = p0.then(function(user) {
            // result is a User
            // Now look for the password recovery question
            if (user) {
                // Found it
                var question = user.get("pwdRecoveryQuestion");
                response.success(question);
            } else {
                response.success(null);
            }
            return null;
        });
});

In the User module, the exported function would look this way.

exports.lookupUserByEmail = function (email)
// returns null when not found
{ 
    var User = Parse.Object.extend("User");
    var query = new Parse.Query(User);

    query.equalTo("email", email);
    var p0 = query.first();
    var p1 = p0.then(
        function(result) {
            return result;
        }
    );
    return p1;
};

So, is there a better solution to this problem?

Esteban
  • 2,540
  • 21
  • 27
LostInTheTrees
  • 1,135
  • 9
  • 19
  • 1
    just to be clear, what do you see as the *"problem"*? – Kevin B Apr 09 '14 at 18:41
  • Seems like code is modularized already? What don't you like about your current approach? – mccainz Apr 09 '14 at 18:42
  • 3
    `I have little interest in becoming an expert in javascript, but I need to do some.` Shame on you :( – Sterling Archer Apr 09 '14 at 18:42
  • @Kevin B, I assumed node require – mccainz Apr 09 '14 at 18:43
  • @KevinB I am not aware of that form. Do you know if it is supported in Parse's JS? I will check of course. – LostInTheTrees Apr 09 '14 at 18:45
  • @everybody I view it as a problem because it seem so cumbersome compared to other languages I deal with. Perhaps this is normal JS. – LostInTheTrees Apr 09 '14 at 18:46
  • @LostInTheTrees what are those other languages? This is the node.js approach to modules. Other modern high level languages, such as Python and Ruby, have similar module systems built in. I think knowing what platforms you're used to (and specifically the benefits you see in them) will help us solve (or demistify) your concerns. – Esteban Apr 09 '14 at 18:57
  • @LostInTheTrees - you might consider codereview.com to get a style question answered. – Hogan Apr 09 '14 at 21:06
  • @Esteban - All the recent coding I have done has been in C, Objective-C or VBA. – LostInTheTrees Apr 09 '14 at 21:06
  • My conclusion from the comments here is that no, there is no better solution. – LostInTheTrees Apr 10 '14 at 21:22
  • @LostInTheTrees I had forgotten about this one. Your usage of the word `better` is ambiguous. If you think it can be improved, please state in what regard. What benefits do other solutions have, in different platforms, that you can't get by doing it this way? – Esteban Apr 18 '14 at 00:10
  • @Esteban Let's take VBA as one example. If I declare a function in a module as public (or just omit "private") it will be available to call from any other module simply by using the name. In C or objective-C, I put the interface in a .h file and then an #include or #import in the .c or .m file where I want to use the interface. I find either of these methods to be more intuitive. – LostInTheTrees Apr 19 '14 at 00:10
  • 1
    @Esteban Maybe a more direct answer to your question is to say that I might expect each module to "require" the modules that it uses. In the method above all of the modules have to be "required" by the main.js and then the entire set of module information is distributed back to the modules so that each module can use the information it wants. I would think it is "better" if each module states or acquires whatever modules it needs. The function of main.js to acquire and then distribute is taken care of by th underlying language/system in the other environments. – LostInTheTrees Apr 19 '14 at 00:18
  • Now I understand much better, thanks. Your last comment has let me know your misunderstanding of node's modularization appraoch. I'll post an answer. – Esteban Apr 19 '14 at 20:04

1 Answers1

1

There's no need to centralize module import in main.js. Every file in node.js is a module that will most likely have private code and export an interface. And it can import as many modules as it needs. This is the simpler way of importing your modules' dependencies:

getRecoveryQuestion definition

var userMod = require('cloud/user.js'),
    constants = require('cloud/constants.js);

Parse.Cloud.define("getRecoveryQuestion", function(request, response) 
{
    var p0 = userMod.lookupUserByEmail(request.params.email);
    var p1 = p0.then(function(user) {
            // result is a User
            // Now look for the password recovery question
            if (user) {
                // Found it
                var question = user.get("pwdRecoveryQuestion");
                response.success(question);
            } else {
                response.success(null);
            }
            return null;
        });
});

The User module would look like this.

cloud/user.js

exports.lookupUserByEmail = function (email)
// returns null when not found
{ 
    var User = Parse.Object.extend("User");
    var query = new Parse.Query(User);

    query.equalTo("email", email);
    var p0 = query.first();
    var p1 = p0.then(
        function(result) {
            return result;
        }
    );
    return p1;
};

Please keep in mind that what you pass to require is a module id. It can be a path (like in the case above) or a module name that will be looked for in a node_modules folder under the root of the project. Several Parse-provided modules will be there for you in their platform. In the previous example, I assumed both constants.js and user.js were in the cloud directory.

However, what you do in your code looks more like dependency injection. There are several reasons you might want to do dependency injection when coding (however, confirm that you do before adding one more layer of complexity to your codebase). Node's module.require implements the module pattern, which makes DI obsolete.

One of the main reasons for dependency injection is testing. You can change what require returns by using a handy library called proxyquire, avoiding the usage of a dependency injection library/framework.

If you have other reasons to do DI (for instance if you still feel uncomfortable about your code's coupling, or whatever), some people have written libraries for it. Check this SO question.

Community
  • 1
  • 1
Esteban
  • 2,540
  • 21
  • 27
  • Thanks. That's what I needed, mostly. I came across this (http://stackoverflow.com/questions/13346046/does-module-require-return-a-copy-of-module-exports-or-a-reference-of-i) which made it clear that "requires" caches the object, so calls from different modules to requires return the same object. I was under the impression that different objects would be returned to multiple requires calls. That means I should be able to put the requires calls directly in the modules with dependencies. – LostInTheTrees Apr 21 '14 at 15:54