-
Notifications
You must be signed in to change notification settings - Fork 73
Support loading of mixins #33
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
Changes from all commits
2043c39
3387eae
a2b4406
4cc7cbb
668bfe2
6b357fe
fa9703c
6191de2
2d6d653
b5067a0
3896756
6753aa1
f2cee03
91c2721
ee4b609
ea64b76
320f746
5e1eb5d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -49,6 +49,7 @@ module.exports = function compile(options) { | |
|
|
||
| var bootScripts = options.bootScripts || []; | ||
| bootDirs.forEach(function(dir) { | ||
| dir = path.resolve(dir); | ||
| bootScripts = bootScripts.concat(findScripts(dir)); | ||
| }); | ||
|
|
||
|
|
@@ -59,19 +60,40 @@ module.exports = function compile(options) { | |
| var modelInstructions = buildAllModelInstructions( | ||
| modelsRootDir, modelsConfig, modelSources); | ||
|
|
||
| var mixinSources = options.mixinSources || modelsMeta.mixins || ['./mixins']; | ||
| var mixinInstructions = buildAllMixinInstructions(appRootDir, mixinSources, options); | ||
|
|
||
| // When executor passes the instruction to loopback methods, | ||
| // loopback modifies the data. Since we are loading the data using `require`, | ||
| // such change affects also code that calls `require` for the same file. | ||
| return cloneDeep({ | ||
| config: appConfig, | ||
| dataSources: dataSourcesConfig, | ||
| models: modelInstructions, | ||
| mixins: mixinInstructions, | ||
| files: { | ||
| boot: bootScripts | ||
| } | ||
| }); | ||
| }; | ||
|
|
||
| function buildAllMixinInstructions(appRootDir, mixinSources, options) { | ||
| var files = options.mixins || []; | ||
| mixinSources.forEach(function(dir) { | ||
| dir = path.resolve(appRootDir, dir); | ||
| files = files.concat(findScripts(dir)); | ||
| }); | ||
|
|
||
| var mixins = {}; | ||
| files.forEach(function(filepath) { | ||
| var ext = path.extname(filepath); | ||
| var name = normalizeMixinName(path.basename(filepath, ext), options); | ||
| mixins[name] = filepath; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As I have commented in [https://github.com/loopbackio/loopback-datasource-juggler/pull/229], the mixin name should be normalized here, not in the juggler. At least these two naming conventions should be supported:
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. BTW the same algorithm can be used to provide a default value for model names as discussed elsewhere (could not find the GH issue, sorry). |
||
| }); | ||
|
|
||
| return mixins; | ||
| }; | ||
|
|
||
| function assertIsValidConfig(name, config) { | ||
| if(config) { | ||
| assert(typeof config === 'object', | ||
|
|
@@ -294,3 +316,19 @@ function loadModelDefinition(rootDir, jsonFile) { | |
| sourceFile: sourceFile | ||
| }; | ||
| } | ||
|
|
||
| function normalizeMixinName(str, options) { | ||
| var normalization = options.normalization; | ||
| if (normalization === false || normalization === 'none') return str; | ||
| if (normalization === 'dasherize') { | ||
| str = String(str).replace(/[\W_]/g, ' ').toLowerCase(); | ||
| str = str.replace(/\s+/g, '-'); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Have you considered using lodash instead of writing the code by hand?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, but for proper normalization, you'd also need underscore.string or something similar. I didn't feel like adding more dependencies to a core component like this.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Adding dependencies to core components is fine, it is better than reinventing the wheel. BTW loopback has both "underscore" and "underscore.string" as dependency. |
||
| } else if (typeof normalization === 'function') { | ||
| str = normalization(str); | ||
| } else { // classify | ||
| str = String(str).replace(/[\W_]/g, ' ').toLowerCase(); | ||
| str = str.replace(/(?:^|\s|-)\S/g, function(c){ return c.toUpperCase(); }); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment as above, we really should not reimplement this ourselves. |
||
| str = str.replace(/\s+/g, ''); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This lines seems redundant to me, as any whitespace should have been already processed.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Which line exactly? and where is the whitespace processed before?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ignore this comment, I don't know why I thought whitespace was already removed. |
||
| } | ||
| return str; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -135,6 +135,7 @@ function setupDataSources(app, instructions) { | |
| } | ||
|
|
||
| function setupModels(app, instructions) { | ||
| defineMixins(app, instructions); | ||
| defineModels(app, instructions); | ||
|
|
||
| instructions.models.forEach(function(data) { | ||
|
|
@@ -145,6 +146,27 @@ function setupModels(app, instructions) { | |
| }); | ||
| } | ||
|
|
||
| function defineMixins(app, instructions) { | ||
| var modelBuilder = app.loopback.modelBuilder; | ||
| var BaseClass = app.loopback.Model; | ||
| var mixins = instructions.mixins || {}; | ||
| var mixinNames = Object.keys(mixins); | ||
| if (modelBuilder.mixins && mixinNames.length > 0) { | ||
| mixinNames.forEach(function(name) { | ||
| var mixin = tryRequire(mixins[name]); | ||
| if (typeof mixin === 'function' || mixin.prototype instanceof BaseClass) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I find this confusing. If mixin is a loopback model, then it must be a function. If the mixin is not a function, then it won't have a
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am taking it back. One of my earlier comments mentions that a mixin can be either a function or an object. If I understand the intention correctly, the latter allows the developer to specify a mixin like an object with some properties and these properties are then added to the target model. I don't know enough about the implementation in juggler, is it necessary that this object has to be an instance of loopback Model?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| var mixinName = mixin.name || mixin.mixinName || name; | ||
| debug('Configuring mixin %s', mixinName); | ||
| modelBuilder.mixins.define(mixinName, mixin); | ||
| } else { | ||
| debug('Skipping mixin file %s - `module.exports` is not a function' | ||
| + ' or Loopback model', | ||
| mixins[name]); | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My understanding of loopbackio/loopback-datasource-juggler#229 is that a mixing can be an object too? Please add debug statements to make troubleshooting easier, especially when the condition on L148 is false. See |
||
| }); | ||
| } | ||
| } | ||
|
|
||
| function defineModels(app, instructions) { | ||
| instructions.models.forEach(function(data) { | ||
| var name = data.name; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| module.exports = function(Model, options) { | ||
|
|
||
| Model.otherMixin = true; | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| // When you create a named function, its name will be used | ||
| // as the mixin name - alternatively, set mixin.mixinName. | ||
|
|
||
| var mixin = function bar(Model, options) { | ||
|
|
||
| Model.barMixin = true; | ||
|
|
||
| }; | ||
|
|
||
| module.exports = mixin; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| module.exports = function(Model, options) { | ||
|
|
||
| Model.exampleMixin = true; | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| module.exports = function(Model, options) { | ||
|
|
||
| Model.timeStampsMixin = true; | ||
|
|
||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add some documentation for
options.mixinstoindex.js.