Skip to content

Instantly share code, notes, and snippets.

@Gozala
Created September 14, 2010 15:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Gozala/9d4cbaba79c57abb3873 to your computer and use it in GitHub Desktop.
Save Gozala/9d4cbaba79c57abb3873 to your computer and use it in GitHub Desktop.

Issues

Current implementation includes some work on capabilities front that ensures privacy of the private APIs. This is nice but it also adds some magic + boilerplate + it is misleading. Here is example:

    const { Trair } = require('traits');
    const Foo = Trait.comose({
      constructor: function FooTrait() {
        .....
      },
      _privateProperty: 'secret',
      publicProperty: 'Helo World'
    });

If you want to export Foo most likely you will write:

    exports.Foo = Foo;

But it is actually wrong since then someone may get access to the privates:

    const MyFoo = require('foo').Foo.compose({ get privateAPI() this });
    MyFoo().privateAPI._privateProperty // -> 'secret'

It can be prevented by:

    exports.Foo = function() Foo.apply(null, arguments);

But this also can trick developers since then:

    const { Foo } require('foo');
    Foo() instanceof Foo // -> false

This also can be addressed by adding one more line:

    exports.Foo.prototype = Foo.prototoype;

Simplified API:

    const FooTrait = Trait.compose({
      _privateProperty: 'secret',
      publicProperty: 'Helo World'
    });
    function Foo() FooTrait.create({ constructor: Foo })
    exports.Foo = Foo;

Current API is also adds overhead in multiple trait compositions:

    const Bar = Foo.resolve({ constructor: null }).compose({
      constructor: function Bar() {
        .....
      },
      ......
    });
    exports.Bar = function() Bar.apply(null, arguments)
    exports.Bar = Bar.prototype;

Simplified API:

    const BarTrait = Trait.compose({
      ....
    }, FooTrait);
    function Bar() {
      let bar = BarTrait.create({ constructor: Bar });
      ....
    }
    exports.Bar = Bar;

One important difference between new and old API is that capabilities are bounded to the traits and constructors can be shared without any wrappers. This also allows model where all the capabilities may be defined with in the constructor instead:

  const BazTrait = Trait.compose({
    _secret: Trait.required,
    doSomethingWithSecret: function() {
      ......
    }
  });
  function Baz() {
    return BazTrait.create({
      constructor: Baz,
      _secret: Cc..... // some chrome privileged thing here
    });
  }

In the last example BazTrait may be defined in a module with no capabilities at all and can be shared with anybody. Baz at the same time can be shared with anybody since all the capabilities are in a jail of the lexical scope.

Simplified API also allows us to implement all the capability restrictions in the separate trait independent from trait implementation itself. Above example will be rewritten:

  const { PublicAPI } = requrie('capabilities');
  const BazTrait = Trait.compose({
    _secret: Trait.required,
    doSomethingWithSecret: function() {
      ......
    }
  }, PublicAPI);
  function Baz() {
    return BazTrait.create({
      constructor: Baz,
      _secret: Cc..... // some chrome privileged thing here
    })._public;
  }

Simplified API is more robust alternative, it's much easier to spot issues and it gives more granular control of capabilities. It does not mimics class based systems and it's absolutely transparent to the developer what happens when constructors are invoked. It also allows cases where constructors are not necessary.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment