[SEMVER-MAJOR] Refactor for modular and pluggable design#181
[SEMVER-MAJOR] Refactor for modular and pluggable design#181davidcheung merged 1 commit intomasterfrom
Conversation
|
Hey @raymondfeng, the proposal looks good from high-level view. I have few questions for you:
What is the purpose of this feature? Could you please provide examples where one would use this feature and how the code will look like?
Again, could you please show some examples of how this would be used?
From your proposal, it is not clear to me what these paths will be used for. Could you please elaborate a bit more? How will these paths relate to the need of having a well-defined order in which handlers are executed? |
|
While we're at it, @davidcheung brought up a good point about config.json vs config.js inconsistencies with our configurations. We should have the new loopback-boot respect base.js files too (config.js, model-config.js, datasources.js) etc. |
079b837 to
5c35225
Compare
|
Can you also make the DYNAMIC_CONFIG_PARAM regex to a bit flexible. Something like /(.)${(\w+)}(.)/ and then you can do configVariable = match(1) + configVariable + match(3) |
3ba32c0 to
1f81231
Compare
|
Connect to #188 |
8555f13 to
41c95cd
Compare
Can you provide an example? |
|
I would like to load sampleData files for memory datasource from different folders based on variables in config.json. Example: file: "samples{$dataFolder}\product.json". In this example, I can keep the value of the variable "dataFolder" defined in config which could be environment specific. |
41c95cd to
51af40f
Compare
| */ | ||
|
|
||
| exports = module.exports = function bootBrowserApp(app, options) { | ||
| exports = module.exports = function bootBrowserApp(app, options, callback) { |
There was a problem hiding this comment.
Since this is for LoopBack Next, can we drop support for callbacks and switch exclusively to Promise-based APIs here in loopback-boot?
There was a problem hiding this comment.
I want to do this incrementally. The current implementation is ready for LoopBack 3.0. We can further clean it up for LoopBack.next if we decide to drop callback style.
|
@davidcheung once #211 is landed, please add a description to 3.0-RELEASE-NOTES.md as part of this pull request (see #181 (comment) for starter). |
96356e5 to
c032849
Compare
|
@slnode test please |
c032849 to
251835f
Compare
251835f to
da89ace
Compare
bajtos
left a comment
There was a problem hiding this comment.
@davidcheung I have reviewed the changes as well as I could. I did not review the overall design again, as I have already commented on it earlier.
Besides the comments listed below, this pull request needs an entry in 3.0-RELEASE-NOTES.md describing the breaking changes.
I think it would be worthwhile to describe the new features in 3.0-RELEASE-NOTES too (the modular design and how to build 3rd party boot plugins). In my experience, the process of writing the documentation usually discover missing pieces and/or rough edges. However, I am fine with leaving the description of the new features out of scope of this pull request.
lib/bootstrapper.js
Outdated
| var debug = require('debug')('loopback:boot:bootstrapper'); | ||
| var Promise = global.Promise || require('bluebird'); | ||
|
|
||
| module.exports = function(options) { |
There was a problem hiding this comment.
I personally prefer to export Bootstrapper directly.
module.exports = Bootstrapper;When I look at the code above, I see 2 places with require('...boostrapper').Bootstrapper and only one place calling the exported function directly.
lib/bootstrapper.js
Outdated
| try { | ||
| loadAndRegisterPlugins(self, options); | ||
| } catch (err) { | ||
| debug(err); |
There was a problem hiding this comment.
Please include a message explaining what went wrong, e.g.
debug('Cannot load & register plugins: %s', err.stack || err);
lib/bootstrapper.js
Outdated
| var path = require('path'); | ||
| var pluginLoader = require('./plugin-loader'); | ||
| var debug = require('debug')('loopback:boot:bootstrapper'); | ||
| var Promise = global.Promise || require('bluebird'); |
There was a problem hiding this comment.
In LoopBack 3.x, we are always using bluebird as our promise implementation. I am proposing to do the same in loopback-boot for consistency.
var Promise = require('bluebird');
lib/bootstrapper.js
Outdated
| } else { | ||
| result = plugin.handler[phase](context); | ||
| if (typeof Promise !== 'undefined') { | ||
| Promise.resolve(result).then(function(value) { |
There was a problem hiding this comment.
Is this trying to implement support for handler functions returning a promise? Here is the version that we use elsewhere in LoopBack (example, it is addressing many subtle edge cases that are handled incorrectly by this code.
if (result && typeof result.then === 'function') {
result.then(
function onPluginPhaseResolved(value) {
done(null, value);
},
function onPluginPhaseRejected(err) {
debug('Unable to invoke %s.%s()', plugin.name, phase, err);
done(err);
});
} else {
done(null, result);
}
lib/bootstrapper.js
Outdated
| }; | ||
|
|
||
| function pluginIteratorFactory(context, phase) { | ||
| return function(plugin, done) { |
There was a problem hiding this comment.
Please use named functions wherever possible, e.g.
return function executePluginPhase(plugin, done) {
// ...
};| expect(app.dummyComponentOptions).to.eql({ option: 'value' }); | ||
|
|
||
| done(); | ||
| var app = executeBundledApp(bundlePath, function(err) { |
| printContextLogs(context); | ||
| done(err); | ||
| }); | ||
| return app; |
There was a problem hiding this comment.
An async function that also returns a value in sync way is very weird. Please pass app to the callback.
app.start(function(err) {
printContextLogs(context);
done(err, app);
});
test/executor.test.js
Outdated
| }), function(err, context) { | ||
| if (err) return done(err); | ||
| expect(app.models.Customer._modelsWhenAttached). | ||
| to.include('UniqueName'); |
There was a problem hiding this comment.
expect(app..._modelsWhenAttached)
.to.include('UniqueName');
test/executor.test.js
Outdated
| function(err) { | ||
| expect(function() { | ||
| if (err) throw err; | ||
| }).to.throw(/Cannot find module \'doesnt-exist\'/); |
There was a problem hiding this comment.
Huh?? This is overcomplicated, just use something like the following:
expect(err && err.message).to.match(/Cannot find module \'doesnt-exist\'/);| ]); | ||
|
|
||
| // bar finished happens in the next tick | ||
| // barSync executed after bar finished |
There was a problem hiding this comment.
I think this change needs to be clearly explained in 3.0-RELEASE-NOTES.md.
22ceee8 to
8138232
Compare
eb391c4 to
70af0d3
Compare
|
@bajtos can you PTAL, the changes are mostly logically separated into their own commit, but probably still quite difficult to navigate due to size of PR :( |
i have only included one change in the |
bajtos
left a comment
There was a problem hiding this comment.
I have reviewed the new commits, left two nit-picks, the rest looks good to me.
After this patch is landed, could you @davidcheung please upgrade eslint-config-loopback to v5 and fix linter errors?
lib/plugins/model.js
Outdated
| }); | ||
|
|
||
| var sortedNames = toposort(edges); | ||
| var _sortedNames = toposort(edges); |
There was a problem hiding this comment.
We usually don't prefix variable names with _.
lib/utils.js
Outdated
| var files = tryReadDir(sourceDir); | ||
| var fixedFile = fixFileExtension(resolvedPath, files, false); | ||
| return (fixedFile === undefined ? resolvedPath : fixedFile); | ||
| var _fixedFile = fixFileExtension(resolvedPath, files, false); |
There was a problem hiding this comment.
We usually don't prefix variable names with _.
|
@davidcheung also please don't forget to squash the commits before landing! |
d531771 to
64a62ba
Compare
- refactor logic of processing artifacts into their own classes - introduce Container as the main class for bootstrapping and build a registry of handlers during boot to organize them by a hierarchy denoted by path - adopt middleware like registration and invocation - container.use(path, handler) - container.run(context, done) - allow more phases during boot - boot is now asynchronous
64a62ba to
ac1571c
Compare
connect to strongloop-internal/scrum-loopback/issues/1136
The current boot process consists of three steps:
It hard-codes logic to handle various artifact types:
The problems with the current design/implementation are:
The PR proposes the following changes:
Containeras the main class for bootstrapping and build a registry of handlers during boot to organize them by a hierarchy denoted by pathmiddlewarelike registration and invocation- explore dependency injection
Connect to #188
Update 16/11/16: