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

[discussion] Overridable public API. #160

Open
trusktr opened this issue Mar 26, 2015 · 9 comments
Open

[discussion] Overridable public API. #160

trusktr opened this issue Mar 26, 2015 · 9 comments

Comments

@trusktr
Copy link
Contributor

trusktr commented Mar 26, 2015

Based from discussions in #152 and #159, making the public API an adapter to a private API might be nice so that people can create "plugins" for the community by simply overriding some or all of dox's public methods.

To restore dox's functionality (i.e. to be able to use dox with multiple types of code in the same running program) I was thinking there could be a simple function that restores all the public methods to their original state simply by re-adapting to the internal API (not by reference, but by closure, for privacy).

@tomek-he-him
Copy link
Contributor

@trusktr @ChiperSoft I'm just wondering – have you seen such plugin architectures in the wild? It sounds awesomely simple, but I'm having second thoughts if it scales well.

I mean – as I see it, it would be unsafe to use two of such "plugins" at once.

@trusktr
Copy link
Contributor Author

trusktr commented Mar 26, 2015

@tomekwi Good question! Can you think of a scenario where it might not scale?

@tomek-he-him
Copy link
Contributor

var dox0 = require("dox");
var dox1 = require("dox-css")(dox0);
var dox = require("dox-markdown")(dox1);

Suppose dox-css and dox-markdown both modify the same function. I would be a shot in the dark if one plugin is compatible with another.

@tomek-he-him
Copy link
Contributor

BTW, I've picked up this require("plugin")(require("base-lib")) pattern in gulp-help. It does its job well in simple cases.

@Twipped
Copy link
Collaborator

Twipped commented Mar 26, 2015

Having an explicit api for extension isn't nessisary. Simply changing dox to invoke functions via this instead of exports would make the lib extensible via mixins.

var dox = Object.assign(
  require('dox'),
  require('dox-css')
  require('dox-markdown')
);

@tomek-he-him
Copy link
Contributor

Good idea!

As a matter of fact, also with require("dox-css")(require("dox")) using this is the only thing required on dox side. The good part is, dox-css can access and extend original dox functions:

// dox-css/index.js
export default (originalDox) => {
  return Object.assign({}
    , originalDox

      // We want to extend `dox.parseComments`
    , { parseComments (...args) {
        // First we apply the original `dox` function
        let result = originalDox.parseComments(...args);
        // Here we can process the result
        return processedResult;
        }

      }
    );
  };

This way we leave dox untouched and extend its functions instead of overriding them (like decorators in python).

It would also solve the problem of scalability to some extent. As long as plugin authors extend functions this way, we can safely do:

let dox = require("dox-markdown")(
  require("dox-css")(
    require("dox")
    )
  )

– in this case dox-markdown extends functions of dox-css which themselves can be extended functions of dox.

@Twipped
Copy link
Collaborator

Twipped commented Mar 26, 2015

It would probably be better if extensions were added via Object.create and then used Object.getPrototypeOfto get at the original.

module.exports = exports = function (dox) {
  dox = Object.create(dox);
  return Object.assign(dox, exports);
}

exports.parseComments = function () {
  var result = Object.getPrototypeOf(this).parseComments.apply(this, arguments);
}

This way you're able to expose the new functions in case other extensions want to use them.

@tomek-he-him
Copy link
Contributor

Good point – that'd be lighter as well as the functions wouldn't need to be copied over and created.

Only the prototype chain might get quite deep – so you never really know if you're taking the real dox or an extended clone. In our example:

// dox-css/index.js
module.exports = exports = function (dox) {
  dox = Object.create(dox);  // This is indeed dox
  return Object.assign(dox, exports);
}

exports.parseComments = function () {
  var result = Object.getPrototypeOf(this).parseComments.apply(this, arguments);
    // dox.parseComments gets called.
}


// dox-markdown/index.js
module.exports = exports = function (dox) {
  dox = Object.create(dox);  // This is dox-css
  return Object.assign(dox, exports);
}

exports.parseComments = function () {
  var result = Object.getPrototypeOf(this).parseComments.apply(this, arguments);
    // doxCSS.parseComments gets called.
}

I don't think that's bad however. After all, every plugin will likely change one aspect of a function. No chance someone will use dox-markdown and dox-creole at the same time.

@trusktr
Copy link
Contributor Author

trusktr commented Mar 27, 2015

👍

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

No branches or pull requests

3 participants