Skip to content

Commit

Permalink
added pod blueprints and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
trabus committed Sep 15, 2014
1 parent 9dff735 commit f5617ce
Show file tree
Hide file tree
Showing 23 changed files with 1,827 additions and 12 deletions.
17 changes: 17 additions & 0 deletions blueprints/component/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@ var Blueprint = require('../../lib/models/blueprint');
var SilentError = require('../../lib/errors/silent');

module.exports = Blueprint.extend({
fileMapTokens: function() {
return {
__templatepath__: function(options) {
if (options.pod) {
return options.podPath+options.dasherizedModuleName;
}
return 'templates/components';
},
__templatename__: function(options) {
if (options.pod) {
return 'template';
}
return options.dasherizedModuleName;
}
};
},

normalizeEntityName: function(entityName) {
entityName = Blueprint.prototype.normalizeEntityName.apply(this, arguments);

Expand Down
2 changes: 1 addition & 1 deletion blueprints/controller/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ var TYPE_MAP = {
basic: 'Controller'
};

module.exports = Blueprint.extend({
module.exports = Blueprint.extend({
beforeInstall: function(options) {
var type = options.type;

Expand Down
17 changes: 17 additions & 0 deletions blueprints/route/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,23 @@ var path = require('path');
var EOL = require('os').EOL;

module.exports = Blueprint.extend({
fileMapTokens: function() {
return {
__templatepath__: function(options) {
if (options.pod) {
return options.podPath+options.dasherizedModuleName;
}
return 'templates';
},
__templatename__: function(options) {
if (options.pod) {
return 'template';
}
return options.dasherizedModuleName;
}
};
},

beforeInstall: function(options) {
var type = options.type;

Expand Down
File renamed without changes.
3 changes: 2 additions & 1 deletion lib/commands/destroy.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ module.exports = Command.extend({

availableOptions: [
{ name: 'dry-run', type: Boolean, default: false },
{ name: 'verbose', type: Boolean, default: false }
{ name: 'verbose', type: Boolean, default: false },
{ name: 'pod', type: Boolean, default: false }
],

anonymousOptions: [
Expand Down
3 changes: 2 additions & 1 deletion lib/commands/generate.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ module.exports = Command.extend({

availableOptions: [
{ name: 'dry-run', type: Boolean, default: false },
{ name: 'verbose', type: Boolean, default: false }
{ name: 'verbose', type: Boolean, default: false },
{ name: 'pod', type: Boolean, default: false }
],

anonymousOptions: [
Expand Down
168 changes: 159 additions & 9 deletions lib/models/blueprint.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@ var any = require('lodash-node/compat/collections/some');
var chalk = require('chalk');
var fs = require('fs-extra');
var glob = require('glob');
var inflector = require('inflection');
var keys = require('lodash-node/compat/objects/keys');
var merge = require('lodash-node/compat/objects/merge');
var minimatch = require('minimatch');
var path = require('path');
var sequence = require('../utilities/sequence');
var stat = Promise.denodeify(fs.stat);
var stringUtils = require('../utilities/string');
var uniq = require('lodash-node/compat/arrays/uniq');
var values = require('lodash-node/compat/objects/values');
var walkSync = require('walk-sync');
var zipObject = require('lodash-node/compat/arrays/zipObject');
var writeFile = Promise.denodeify(fs.outputFile);
var removeFile = Promise.denodeify(fs.remove);
var SilentError = require('../errors/silent');
Expand All @@ -37,12 +41,12 @@ module.exports = Blueprint;
blueprints/controller
├── files
│   ├── app
│   │   └── controllers
│   │   └── __path__
│   │   └── __name__.js
│   └── tests
│   └── unit
│   └── controllers
│   └── __name__-test.js
│   └── __test__-test.js
└── index.js
```
Expand All @@ -51,10 +55,31 @@ module.exports = Blueprint;
`files` contains templates for the all the files to be
installed into the target directory.
The `__name__` placeholder is subtituted with the dasherized
The `__name__` token is subtituted with the dasherized
entity name at install time. For example, when the user
invokes `ember generate controller foo` then `__name__` becomes
`foo`.
`foo`. When the `--pod` flag is used, for example `ember
generate controller foo --pod` then `__name__` becomes
`controller`.
The `__path__` token is substituted with the blueprint
name at install time. For example, when the user invokes
`ember generate controller foo` then `__path__` becomes
`controller`. When the `--pod` flag is used, for example
`ember generate controller foo --pod` then `__path__`
becomes `foo` (or `<podModulePrefix>/foo` if the
podModulePrefix is defined). This token is primarily for
pod support, and is only necessary if the blueprint can be
used in pod structure. If the blueprint does not require pod
support, simply use the blueprint name instead of the
`__path__` token.
The `__test__` token is substituted with the dasherized
entity name at install time. This token is primarily for
pod support and only necessary if the blueprint requires
support for a pod structure. If the blueprint does not
require pod support, simply use the `__name__` token
instead.
## Template Variables (AKA Locals)
Expand Down Expand Up @@ -223,6 +248,9 @@ Blueprint.prototype.install = function(options) {
var ui = this.ui = options.ui;
var intoDir = options.target;
var dryRun = options.dryRun;
this.pod = options.pod;
this.podPath = generatePodPath();
this.hasPath = hasPathToken(this.files());
this.project = options.project;
this.testing = options.testing;

Expand Down Expand Up @@ -276,6 +304,10 @@ Blueprint.prototype.install = function(options) {
options.entity.name = this.normalizeEntityName(options.entity.name);
}

if (!this.hasPath && this.pod) {
ui.writeLine(chalk.yellow('You specified the pod flag, but this blueprint does not support pod structure. It will be generated with the default structure.'));
}

var locals = this._locals(options);

return Promise.resolve()
Expand All @@ -293,9 +325,9 @@ Blueprint.prototype.uninstall = function(options) {
var ui = this.ui = options.ui;
var intoDir = options.target;
var dryRun = options.dryRun;
var packageName = options.project.name();
var moduleName = options.entity && options.entity.name || packageName;
var locals = { dasherizedModuleName: stringUtils.dasherize(moduleName) };
this.pod = options.pod;
this.podPath = generatePodPath();
this.hasPath = hasPathToken(this.files());
this.project = options.project;

var actions = {
Expand Down Expand Up @@ -327,6 +359,12 @@ Blueprint.prototype.uninstall = function(options) {
options.entity.name = this.normalizeEntityName(options.entity.name);
}

if (!this.hasPath && this.pod) {
ui.writeLine(chalk.yellow('You specified the pod flag, but this blueprint does not support pod structure.'));
}

var locals = this._locals(options);

return Promise.resolve()
.then(this.beforeUninstall.bind(this, options))
.then(this.processFilesForUninstall.bind(this, intoDir, locals)).map(commit)
Expand Down Expand Up @@ -368,6 +406,63 @@ Blueprint.prototype.afterUninstall = function() {};
*/
Blueprint.prototype.locals = function() {};

/*
Hook to add additional or override fileMapTokens.
The fileMapToken order should match fileMapTokenValues order
if overriding. The fileMapTokenValues order should match fileMapTokens
order. Default is expected, pods is necessary if pod structure
is supported for blueprint.
@method fileMapTokens
@return {Object}
*/
Blueprint.prototype.fileMapTokens = function() {
};

/*
@private
@method _fileMapTokens
@param {Object} options
@return {Object}
*/
Blueprint.prototype._fileMapTokens = function(options) {
var hasPathToken = this.hasPath;
var standardTokens = {
__name__: function(options) {
if (options.pod && hasPathToken) {
return options.blueprintName;
}
return options.dasherizedModuleName;
},
__path__: function(options) {
if (options.pod && hasPathToken) {
return options.podPath+options.dasherizedModuleName;
}
return inflector.pluralize(options.blueprintName);
},
__test__: function(options) {
return options.dasherizedModuleName;
}
};

var customTokens = this.fileMapTokens(options) || options.fileMapTokens || {};
return merge(standardTokens, customTokens);
};

/*
Used to generate fileMap tokens for mapFile.
@method generateFileMap
@param {Object} fileMapVariables
@return {Object}
*/
Blueprint.prototype.generateFileMap = function(fileMapVariables){
var tokens = this._fileMapTokens(fileMapVariables);
var fileMapValues = values(tokens);
var tokenValues = fileMapValues.map(function(token) { return token(fileMapVariables); });
var tokenKeys = keys(tokens);
return zipObject(tokenKeys,tokenValues);
};

/*
@method buildFileInfo
@param {Function} destPath
Expand Down Expand Up @@ -474,8 +569,14 @@ Blueprint.prototype.processFilesForUninstall = function(intoDir, templateVariabl
@return {String}
*/
Blueprint.prototype.mapFile = function(file, locals) {
var pattern, i;
var fileMap = locals.fileMap || { __name__: locals.dasherizedModuleName };
file = Blueprint.renamedFiles[file] || file;
return file.replace(/__name__/g, locals.dasherizedModuleName);
for(i in fileMap){
pattern = new RegExp(i, 'g');
file = file.replace(pattern, fileMap[i]);
}
return file;
};

/*
Expand All @@ -489,13 +590,21 @@ Blueprint.prototype._locals = function(options) {
var moduleName = options.entity && options.entity.name || packageName;

var sanitizedModuleName = moduleName.replace(/\//g, '-');
var fileMapVariables = {
pod: this.pod,
podPath: this.podPath,
blueprintName: this.name,
dasherizedModuleName: stringUtils.dasherize(moduleName)
};

var fileMap = this.generateFileMap(fileMapVariables);
var standardLocals = {
dasherizedPackageName: stringUtils.dasherize(packageName),
classifiedPackageName: stringUtils.classify(packageName),
dasherizedModuleName: stringUtils.dasherize(moduleName),
classifiedModuleName: stringUtils.classify(sanitizedModuleName),
camelizedModuleName: stringUtils.camelize(sanitizedModuleName)
camelizedModuleName: stringUtils.camelize(sanitizedModuleName),
fileMap: fileMap
};

var customLocals = this.locals(options);
Expand Down Expand Up @@ -833,3 +942,44 @@ function generateLookupPaths(lookupPaths) {
lookupPaths = lookupPaths.concat(Blueprint.defaultLookupPaths());
return uniq(lookupPaths);
}

/*
Looks up podModulePrefix and returns string path
for pod generation
@private
@method generatePodPath
@return {String}
*/
function generatePodPath() {
var appPath = path.join(process.cwd(), 'app', 'app.js');
var existence = /podModulePrefix/g;
var result = '';
var appContent;

if(!fs.existsSync(appPath)){
return result;
} else {
appContent = fs.readFileSync(appPath, 'utf-8');
}
if (!existence.test(appContent)) {
//throw new SilentError('podModulePrefix is not present');
} else {
// TODO: Look this up from config eventually
result = appContent.match(/podModulePrefix:.*\/+(.\w*)/)[1]+'/';
}
return result;
}

/*
Looks for a __path__ token in the files folder. Must be present for
the blueprint to support pod tokens.
@private
@method hasPathToken
@param {files} files
@return {Boolean}
*/
function hasPathToken(files) {
return files.join().match(/__path__/);
}
Loading

0 comments on commit f5617ce

Please sign in to comment.