cujojs / wire

A light, fast, flexible Javascript IOC container
Other
861 stars 68 forks source link

'import' a context into another #161

Closed ouadi closed 10 years ago

ouadi commented 10 years ago

Hello dears,

I'm looking for a way to realize the following use-case: 1) I have many modules and each one of them has a wire spec that exposes its components 2) To assemble an application, I select modules and use their wire-spec 3) The wire-spec of the application is the merge of wire-specs of used modules: 3.1) I start by 'requiring' the wire-spec of each module as objects 3.2) I merge the objects 3.3) I return the result as the object defining the wire-spec of the application

I have read several times wire documentation hoping to find a 'native' way to do the above but I failed so far to find one.

A 'native' way would be a factory like the 'wire' factory but instead of creating a child-context for each module, I'm looking to see the components of each module as direct components of the application context.

Spring, for instance, allows importing a context definition into another one and the result is as if the content of the imported context has been inlined with the importing context.

Warm regards

Younes

briancavalier commented 10 years ago

Hey @ouadi, there are a couple of ways you can do this, depending on whether you use wire programmatically, or as an AMD plugin. Programmatically, you can pass an array of wire specs to the wire function, and wire will mix them (ie "inline" as you put it) together for you, then wire the resulting combined wire spec. That's in contrast to the wire factory, which, as you pointed out, creates child contexts.

For example:

var wire = require('wire');
var spec = require('my/app/spec');
var moreComponents1 = require('my/app/more1');
var moreComponents2 = require('my/app/more2');

var promise = wire([spec, moreComponents1, moreComponents2]);

promise.then(function(context) {
    /* context contains all components from spec, moreComponents1, and moreComponents2 */
});

If there are components of the same name in spec, moreComponents1, and moreComponents2, the one furthest "to the right" will override.

When using wire as an AMD plugin, you can use a comma-separated list of spec module ids as a shortcut for the programmatic approach above:

curl(['wire!my/app/spec,my/app/more1,my/app/more2'], function(context) {
        /* context contains all components from spec, moreComponents1, and moreComponents2 */
});

Hope that helps!

ouadi commented 10 years ago

Hi @briancavalier,

Thank you for your prompt feedback.

The two solutions are good and definetly, I will use them to handle other use-cases. However, they don't fulfill my need for the presented use-case.

For my application, I prefer to assemble it in a declative way. Idealy, the application spec defines no component. Its main purpose is serving as an assembly descriptor.

For example, I would like to write the application spec like the following :

define({
    $import: 'module1',
    $import: 'module2',
    $import: 'module3',
});

The above will be feed to wire(), either programmatically or as an AMD plugin, in order to build up the entire application.

The end result is that all components wired within module1, module2 and module3 will be direct components of the application context.

As such, components in module3 can reference, for instance, a component named some-comp. That component would be provided either by module1 or module2.

Thank you

Younes

briancavalier commented 10 years ago

@ouadi, there's currently no way to do an "import" from within a wire spec.

For example, I would like to write the application spec like the following

We could consider adding something like your example, although it would have to be more like the following since duplicate object keys are not allowed:

define({
    $import: ['module1', 'module2', 'module3']
});

Would you be up for submitting a PR for that?

To avoid being blocked by this, you can achieve the same thing without any changes to wire using your favorite JS mixin function:

define(function(require) {
    var mixin = require('your/favorite/library/mixin');
    return mixin(
        require('module1'),
        require('module2'),
        require('module3')
    );
});

Or if you prefer "classic" AMD:

define(['your/favorite/library/mixin', 'module1', 'module2', 'module3', function(mixin, module1, module2, module3) {
    return mixin(
        module1,
        module2,
        module3
    );
});

The end result is that all components wired within module1, module2 and module3 will be direct components of the application context. As such, components in module3 can reference, for instance, a component named some-comp. That component would be provided either by module1 or module2.

All of the above techniques will achieve this (including the previous programmatic and AMD versions). So, your goal is doable, it's just that there's no built-in syntactic sugar for it currently.

ouadi commented 10 years ago

Hi @briancavalier,

Thank you again for your prompt feedback.

I will consider your request for submitting a PR.

I will opt for the solution you described in your first reply as the ones you described in your last reply are what I'm doing now. I want to avoid it as it push for a mix between a raw AMD and a real IoC.

I will you let know once I come up with something.

Thank you again for your help.

Warm regards

Younes