-
Notifications
You must be signed in to change notification settings - Fork 367
Basic plugin architecture #201
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
Conversation
Similar to http://mongoosejs.com/docs/plugins.html Next, loopback-boot should be updated to support loading plugins from dirs.
|
I found myself mixing in functionality all the time, and this provides a lightweight and flexible way to do so. There should probably be some common core plugins added to ./plugins. Additionally, we should see if the before/after hooks can be made more flexible - monkey-patching them in plugins is probably not the best way to go forward, but it works for now. @raymondfeng @ritch looking forward to your input - I'm itching to tackle loopback-boot integration. |
|
@fabien Great stuff. Thanks! I have a few questions/suggestions:
|
|
@raymondfeng I know about mixins, they seem to be a tad more involved but less generic and thus less flexible. In my plugin method I can do - dynamically - whatever I want, not just extend/mixin. And there's options to configure the plugin at runtime. However, I'd be all for covering both concepts under the Also, any ideas on fixing the hook limitation of one method/fn to be defined, without breaking BC? |
|
Cool, +1 to support both styles. A mixin can be defined as:
A mixin can be loaded from:
BTW, we're working on improving hooks so that they will be close to middleware styles and should allow chaining. |
|
@raymondfeng regarding: A abstract model class or corresponding LDL (json) definition Could you post a gist of how those 2 definitions would look? I'm not sure how the model class is set up - which dependencies to require to create such a model in code in a simple manner. It seems a bit cumbersome for this purpose. |
|
Here is what I have in mind: LDL format: {
name: 'MyModel',
properties: {
...
}
mixins: ['TimeStamp', 'MyMixIn']
}Abstract model class: MyModel.mixin(TimeStamp, MyMixIn, ...); |
|
That's not how these are defined in a module, right? It looks to me that's how you'd actually apply - mix-in - them. I was referring to the code in such modules, or in the .json LDL for the mixin definition (like MyMixin) itself. Also, in LDL I'd prefer an object over an array, to be able to pass in an options object. Do you see a need to 'classify' the mixin names (as loaded from the module file name)? |
|
Looking at http://docs.strongloop.com/display/LB/LoopBack+Definition+Language#LoopBackDefinitionLanguage-Mixinginmodeldefinitions it appears that at a minimum, you'd need a modelBuilder instance to declare such a mixin in code. For little gains over the LDL in JSON. Therefor, I think we should support these 3 types of mixins:
|
Mixin types: module function, module object, LDL json object.
|
@raymondfeng I implemented 1, 2 and 3 but it looks like there's a bug in the 'old' mixin implementation (currently still unresolved) - see the commented-out tests, where a LDL-defined mixin fails to actually mix in the properties. Unless this is delayed or implemented differently, I expected the properties to be hard-wired after applying the mixin. |
|
@raymondfeng I updated loopback and loopback-boot to support mixin loading. |
|
I agree this should be also avalible outside loopback-boot |
|
If we step back, the mixin function is basically a hook to the model definition. I would like to generalize the idea so that we don't have to introduce too many concepts/layouts. A few opinions/questions:
module.exports = function(app) {
app.models.forEach(function(model) {
...
});
}
|
|
Makes sense, i will give 1. a try, because i really need this functionality |
|
Well there's one problem, how to support input parameters and to which models mixins apply using boot. |
|
Where are the input params from? Can you give me one example? For the models that need the mixins, we should be able to configure them via model definitions. Simply speaking, we need to have a way to discover a bunch of JS files and invoke them following a method signature as the contract. |
|
@raymondfeng please keep the current implementation as much as possible, there are reasons for doing this having the option to call And mixins should definitely accept options/params, not just from LDL, but also at runtime Regarding 1. you can always use mixins.define() to define mixins in-code (on the browser side). 2. ties loading of mixins totally to loopback(-boot), and cripples the ability to use mixins seperately. How else would they be of any use to juggler, if there's no way to load them (other than to define them)? |
|
@raymondfeng re: For the models that need the mixins, we should be able to configure them via model definitions. To me mixins are vastly different from the mixins than the ones outlined here: http://docs.strongloop.com/display/LB/LoopBack+Definition+Language#LoopBackDefinitionLanguage-Mixinginmodeldefinitions - this is just some syntactic-sugar to me. I don't think these are useful at all - zero use-case for me, because of their inherent static nature. For real-world usage, a plugin/mixin should be dynamic - see Mongoose's vast library of plugins of what you're able to do. I was hoping to bring this to loopback's juggler, and perhaps offer some useful core mixins as part of the module itself (hence the loading code here, instead of elsewhere). Not to mention, as I pointed out before, I don't think they actually work as expected (see #issuecomment-51322687 above). |
|
@fabien Maybe what you have in mind for the mixin function is broader than I think. Let's first make sure if that's the case.
|
|
@raymondfeng what you describe is exactly what I implemented AFAIK, even the api to register functions by name. Is this what you have in mind as well? Or is this where our thoughts diverge? Note that the old-style mixin functionality (as outlined in the docs) is also supported, but even now I'm not sure if that code actually works (the commented-out tests I was referring to above). Also, as you can see, I'm using the mixin functions to internally implement the old-style extend-functionality, as well as object-based extend, which keeps the code concise. |
|
I had some problems if i defined mixins in model json file, more On Fri, Aug 8, 2014 at 12:03 AM, Fabien Franzen [email protected]
-----BEGIN PGP PUBLIC KEY BLOCK----- mQENBFEY1PEBCADPOfERF2wo4qeoq9L1m2z4pKfWqNd4B6BsoFUWPNd7BXmY+9JG |
|
@fabien I think we are mostly on the same page. The only difference in opinions is that I think the discovery and loading of mixin functions should live outside of the juggler. To use juggler as a library without loopback, the mixin js files/npm modules can be required and registered. Or the hosting app can provide their own convention for auto discovery and registration. The old style of mixin is just a special case of mixin function. It can be viewed that there is a built-in mixin function that can take another model and add all properties/methods to the target model. |
|
@offlinehacker my guess is that mixins defined in json/LDL are applied at a slightly different stage - methods like deleteById are probably mixed in by juggler itself after that. If so, we should find a better point (~event) in the life-cycle of a model to apply mixins. @raymondfeng thoughts on that? @raymondfeng re: loading of mixin functions - I don't see this as a big deal, those < 40 LOC make no assumptions about the LB app-structure whatsoever, it is very generic, while still auto-handling the different types of mixins (LDL/JSON, Object, Function). Without it, you'd have to do that on your own whenever you need it outside of LB. What about my reasoning that there will be more core mixins supplied with juggler in the future, and that they should be auto-loaded from /lib/mixins? It would immediately lead to code-duplication, if we were to split that off to lb-boot. On the other hand, if you think this is the way to go, please go ahead and make the required changes ;-) I'd like to see it merged ... |
|
@fabien You are right, the CRUD methods are mixed into the model class when it's attached to a data source. The connector behind the data source provides a DataAccessObject object, which is a mixin. We can now unify these things. For the loading, it should only apply to mixin (provider) functions. I would like to keep LDL mixin as regular models. One model can mix in other models or a mixin function can be applied. BTW, we're trying to come up a component architecture so that NPM (or local) modules following the component layout/manifest can be automatically recognized by loopback so that models/mixins/datasources packaged in a component will be discovered by loopback. |
|
@raymondfeng what's weird is that I'm using In the case of loading only mixin functions, I guess it's fine to remove the loading code here. I'd like to move on - would you like to change the code as you see fit? Now, what I'm really looking forward to is having loopback-boot discover those npm modules, but I'd be fine with just listing the module names in config.json and go from there (default dir layout). |
|
The |
|
I did some clean up, see https://github.com/strongloop/loopback-datasource-juggler/tree/fabien-feature/plugins |
|
@raymondfeng thanks, that looks good! So I guess lb-boot is up next? Shall I give it a shot? |
|
@raymondfeng given that I'm using dataSourceAttached, why wouldn't it work as expected? |
|
Check: strongloop/loopback-boot#33 - I enforced mixin name formatting, to account for files like foo-bar.js (mixin: FooBar). |
|
@raymondfeng you mentioned: BTW, we're working on improving hooks so that they will be close to middleware styles and should allow chaining. How far along is this? I'm tempted to fix this otherwise. |
|
I was evaluating hooks-js but it doesn't handle static methods. I forked it into https://gist.github.com/raymondfeng/51bf3f9ed2599e4ec1ad. Feel free to start from scratch or go from what I have. |
Similar to http://mongoosejs.com/docs/plugins.html
Next, loopback-boot should be updated to support loading plugins from dirs.