Question

I'm trying to get a dynamic service locator written in JavaScript using Harmony Proxies (Node.js). Basically you would create a new container:

 var container = new Container();

You would then be able to set and get values like a traditional service locator:

 container.set('FM', {});
 container.get('FM');
 container.get('FM', function(FM) {

 });

You can even have namespaces that acts like sub-objects:

 container.set('FM.Object', {});

The problem is with the dynamic aliases.

 var App = container.alias('App');

When creating an alias, it creates a new proxy object where you can manipulate it like a traditional object, but it's an alias to get() and set() methods.

Instead of:

 container.get('App.Hello'); 

With an alias, you would use:

 App.Hello

The problem is with deep namespaces. Let's say you're trying to access App.Hello.World.Controller, because it goes through the proxy one namespace at a time and not all at once (like that full namespace). How would I know if the user is calling App.Hello to retrieve the value stored in the service locator, or if the user wants to continue accessing deeper namespaces? You can't (From what I've tried).

What other way could you accomplish this?

Having the syntax App.Hello.World which would return either a proxy, if it's going to access a deeper namespace, or the value stored in the service locator.

  var App = container.alias('App');
  App.Controller.Home.Method.Index
  // It would go through:
  App -> Proxy
  App.Controller -> Proxy or Value?
  App.Controller.Home -> Proxy or Value?
  App.Controller.Home.Method -> Proxy or Value?
  App.Controller.Home.Method.Index -> Proxy or Value?

Right now, I've assembled a convention for dealing with the two options. You would use singular names for retrieving the value and plural for returning a proxy. This was just a quick "hack" as it's not very effective.

(If you need clarification or more info just let me know)

Was it helpful?

Solution

Overview of the Problem

Let me see if I understand you correctly. It sounds like you want properties accessed "together" to be considered part of a single key for controller.get. Take the following example:

controller.set('App.Foo.Bar', { Zap: 1 });
controller.set('App.Foo.Bar.Zap', 2);

var App = controller.alias('App');

var Bar = App.Foo.Bar;
var Zap = Bar.Zap;

var Zap2 = App.Foo.Bar.Zap;

console.log(Zap, Zap2);
// should log: 1, 2

If the above behavior is what you want, then it's not possible (with that syntax). JavaScript just doesn't distinguish between App.Foo.Bar.Zap and var Bar = App.Foo.Bar; Bar.Zap. There's nothing in the language that would allow you to differentiate between properties that were accessed "together" (var Bar = App.Foo.Bar) and one that was accessed "separately" (Bar.Zap).


Proposal

You can, however, get close by modifying your syntax. I thought about a few different possibilities, and here's the best I could come up with:

controller.set('App.Foo.Bar', { Zap: 1 });
controller.set('App.Foo.Bar.Zap', 2);

var App = controller.alias('App');

// Use parens to note that we are ending the key name
// and want it to retrieve a value.
var Bar = App.Foo.Bar();
var Zap = Bar.Zap;

var Zap2 = App.Foo.Bar.Zap();

console.log(Zap, Zap2);
// should log: 1, 2

If you add parentheses after a property retrieval, as above, then you can use this syntax as a distinguishing note to your proxy that that means to retrieve the value from the controller.


Implementation

Here's the code that would make that possible:

var Container = (function() {

    return function Container() {

        var map = { };

        this.get = function get(key) {
            return map[key];
        };

        this.set = function set(key, value) {
            map[key] = value;
        };

        this.alias = function alias(name) {
            return createAlias(this, name);
        };

    };

    function createAlias(container, aliasName) {
        return new Proxy(
            function() {
                return container.get(aliasName);
            },
            {
                get: function(target, name) {
                    return createAlias(container, aliasName + '.' + name);
                },
                set: function(target, name, value) {
                    container.set(aliasName + '.' + name, value);
                }
            }
        );
    }

})();

You can set with = and retrieve with ():

var container = new Container();
container.set('App.Controller.Home.Method.Index', 5);

var App = container.alias('App');

App.Controller.Home.Method = 7;

console.log(
    App.Controller.Home.Method(),      // => 7
    App.Controller.Home.Method.Index() // => 5
);

Final Note

I'm sure you're aware that Proxies are experimental, but I just want to emphasize that this particular feature of ES6 is still quite in flux. It could be that the current direct proxies proposal ends up being included in the language, but it's also very possible that proxies in ES will change significantly over the next year. (There have been vigorous discussions on the ECMAScript mailing list over the past month regarding the proxies proposal.) At least with Node you can just keep using an old version of Node if proxies change dramatically (while you work on updating an old implementation).

Hope this helps!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top