Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Module for optional dependencies #16

Closed
wants to merge 4 commits into from
Closed

Conversation

javruben
Copy link

@javruben javruben commented Oct 1, 2010

Hi,

I've added a (ridiculously simple) module that allows you to fetch a reference to a module only when it's already in the cache. I believe this is a common usecase for maybe some of the bigger frameworks. This problem is not easily solvable with module modifiers because it would require knowledge about other modules that is not available.

Kind Regards,

Ruben Daniels
Ajax.org

@jrburke
Copy link
Member

jrburke commented Oct 3, 2010

Interesting! I'm playing around with this, and it looks like it can just be reduced to the following:

(function () {
    require.plugin({
        prefix: "optional"
    });
}());

I have done an initial test and that seems to work. I am still looking at doing a little bit more on the testing, then need to figure out the documentation. If all that feels good, then I will look at pushing this new plugin. At first thought, I am hesitant to use "optional" for the plugin name. I do not want to give the impression it will try to load the module and if it does not, then it does not error out. That is what I would think optional could mean. This plugin is more like "if already in the defined cached, return it". Maybe an "ifdef" plugin. :)

So any thoughts on names would be appreciated.

@javruben
Copy link
Author

javruben commented Oct 3, 2010

Good points. I suspected it could be reduced, but I didn't know the full implications, so I left the methods in. What do you think about the name "cache"? That should indicate clearly that we'll only fetch the module from the cache.

@javruben
Copy link
Author

javruben commented Oct 3, 2010

Actually, "ifdef" is growing on me. I like it better than cache because it clearly specifies the use of the module and is less confusing.

@jrburke
Copy link
Member

jrburke commented Nov 2, 2011

This is possible to do now by using require.specified() in a loader plugin. I'll leave it as an exercise for the reader to implement that type of plugin though. This kind of plugin has not come up much in the requirejs list.

@jrburke jrburke closed this Nov 2, 2011
@fheidrich
Copy link

Hi James,

I was hoping that a simpler mechanism could be provided. My idea was to use a prefix (like text! or order!), for example "optional!".

In Orion we are using requirejs heavily, here is an real case example that could benefit from this support:

In the editor module offers different features such as rulers and tooltips. If tooltips are present then tooltips are used, but if tooltips are not present then I still want everything to work (except, of course, no tooltips).

The define for the rulers module looks like this:

define(['orion/textview/tooltip'], function(mTooltip) {
//do all the work
}

If "orion/textview/tooltip.js" is not available then nothing work.

I wish I could use:

define(['optional!orion/textview/tooltip'], function(mTooltip) {
//do all the work
if (mTooltip) {
//add tootips support
}
}

In this case, if someone adopting the editor component from orion and does not want tooltips then they do not need to take the tooltip.js file at all.
see https://bugs.eclipse.org/bugs/show_bug.cgi?id=364401

Please let me know if that makes sense to you

@jrburke
Copy link
Member

jrburke commented Nov 23, 2011

@fheidrich, using require.specified() inside an 'optional' loader plugin might work then. I'm not sure if 'optional' is quite right, I think i 'ifdef' is closer, really 'ifspec' because require.specified just indicates if some other module has specified that the module in question has been asked to be loaded.

So for sake of example, assume it is 'ifspec', and it takes two module IDs, the one to check if specified and the other to be loaded if the specified one has been specified. In the example you give, if tooltip has been specified you want to get a handle on tooltip, but there could be other examples where you want to get a handle on a different module if a particular one was specified. But for your example:

define(['ifspec!orion/textview/tooltip:orion/textview/tooltip'], function (tooltip) {

});

Or you could just say the API for it is just one module, like your example, and it just means "if this module has been specified by some other module, give me a handle on it".

The possible downside with this approach is that it only works if whatever module that was going to specify the use of tooltip would have asked for it before this define() call is run.

What might be better long-run is to use the has() approach, and then also create a has plugin that reads the has config, and a particular use of orion would map the 'hasConfig' module to their desired config. So something like this:

//in a has.js file
define(['hasConfig'], function (hasConfig) {
    var testResults = {};

    function has(testName) {
        if (hasConfig.hasOwnProperty(testName)) {
            return hasConfig[testName];
        } else {
            return testResults[testName];
        }
    }

    //Adds a testName, with testFunc returning true or false with
    //the value of the test.
    has.add = function (testName, testFunc) {
        testResults[testName] = !!testFunc();
    };

    has.load = function(name, require, load, config) {
        //name is format testName?trueModuleName:falseModuleName.
        //parse this to variables matching the names above
        var moduleId;

        if (has(testName)) {
            if (trueModuleName) {
                moduleId = trueModuleName;
            }
        } else if (falseModuleName) {
            moduleId = falseModuleName;
        }

        if (moduleId) {
            require([moduleId], load);
        } else {
            load();
        }
    };

    return has;
}

Then, for orion apps that used tooltips, their hasConfig.js would look like:

define({
    tooltips: true
});

And then in the ruler.js module:

define([has!tooltips?orion/textview/tooltip'], function (tooltip) {
    if (tooltip) {
        //use tooltips.
    }
});

There would be a bit more work in that has loader plugin to work well with builds, but if this makes sense, then I can look at coding up that module. I keep talking about it but just need to do it at some point.

This approach also then allows you more fine-grained logic within a module that does not necessarily need to get a handle on the tooltip module, but needs to do some extra work if tooltips are in play:

define(['has'], function (has) {
    if (has('tooltips')) {
        //do some extra work related tooltips
    }
});

And the bonus part, that if(has('tooltips') block can be removed by the optimizer, so there is some "dead code removal" with this approach.

@neonstalwart
Copy link
Contributor

+1 to the has plugin approach - this is how dojo does it with the config, ternary style dependency and dead code removal. an alternative approach is a service locator or ioc container. when i looked at orion's code (quite a few months ago) it looked like it was heavily leveraging a service locator already - not sure how much has changed since then. overall, the dead code removal made possible using has makes it very appealing to my mind but i guess it depends on your specific needs.

@jzaefferer jzaefferer mentioned this pull request Jul 18, 2013
8 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants