From a04056c87e91a62232af847c704a8c61114e6de1 Mon Sep 17 00:00:00 2001 From: cueedee Date: Sun, 21 Aug 2016 21:38:47 +0200 Subject: [PATCH] feat(generators/api): add a subgenerator for creating API instances --- generators/api/index.js | 179 ++++++++++++++++++++++++++++ generators/api/templates/api.coffee | 54 +++++++++ lib/youtil.js | 14 +++ package.json | 1 + 4 files changed, 248 insertions(+) create mode 100644 generators/api/index.js create mode 100644 generators/api/templates/api.coffee diff --git a/generators/api/index.js b/generators/api/index.js new file mode 100644 index 00000000..c6c3b4cf --- /dev/null +++ b/generators/api/index.js @@ -0,0 +1,179 @@ +'use strict'; + +// +// Yeoman bat:api sub-generator. +// + +var generators = require( 'yeoman-generator' ) +, yosay = require( 'yosay' ) +, youtil = require( './../../lib/youtil.js' ) +, chalk = require( 'chalk' ) +, _ = require( 'lodash' ) +; + +var decapitalize = require( 'underscore.string/decapitalize' ); + +var ApiGenerator = generators.Base.extend( + { + constructor: function () + { + generators.Base.apply( this, arguments ); + + this.description = this._description( 'API-instance' ); + + this.argument( + 'apiName' + , { + type: String + , required: false + , desc: 'The name of the API-instance to create.' + } + ); + + // Also add 'apiName' as a - hidden - option, defaulting to the positional argument's value. + // This way `_promptsPruneByOptions()` can filter away prompting for the API name too. + // + this.option( + 'apiName' + , { + type: String + , desc: 'The name of the API-instance to create.' + , default: this.apiName + , hide: true + } + ); + + // Normal options. + // + this.option( + 'description' + , { + type: String + , desc: 'The purpose of this API.' + } + ); + + this.option( + 'url' + , { + type: String + , desc: 'The base URL for this API.' + } + ); + + } + + , initializing: function () + { + this._assertBatApp(); + + // Container for template expansion data. + // + this.templateData = {}; + } + + , prompting: + { + askSomeQuestions: function () + { + // Ask only those question that have not yet been provided with answers via the command line. + // + var prompts = this._promptsPruneByOptions( + [ + { + type: 'input' + , name: 'apiName' + , message: 'What is the name of this API-instance you so desire?' + , default: youtil.definedToString( this.options.apiName ) + , validate: youtil.isIdentifier + , filter: function ( value ) + { + return decapitalize( _.trim( value ).replace( /api$/i, '' )); + } + } + , { + type: 'input' + , name: 'description' + , message: 'What is the purpose (description) of this API?' + , default: function ( answers ) + { + return ( + youtil.definedToString( this.options.description ) + || ( + 'A collection of services\' endpoints available on the ' + + ( answers.apiName || this.templateData.apiName ) + + ' API.' + ) + ); + }.bind( this ) + , validate: youtil.isNonBlank + , filter: youtil.sentencify + } + , { + type: 'input' + , name: 'url' + , message: 'What is the base URL for this API? ' + chalk.gray( ' - please enter as code:' ) + , default: youtil.definedToString( this.options.url ) + , validate: youtil.isCoffeeScript + } + ] + ) + ; + + if ( prompts.length ) + { + // Have Yeoman greet the user. + // + this.log( yosay( 'So you want a BAT API-instance?' ) ); + + return ( + this + .prompt( prompts ) + .then( function ( answers ) { _.extend( this.templateData, answers ); }.bind( this ) ) + ); + } + } + } + + , configuring: function () + { + var data = this.templateData + , apiName = data.apiName + ; + + _.extend( + data + , { + className: _.capitalize( apiName ) + 'Api' + , fileBase: _.kebabCase( _.deburr( apiName )) + + , userName: this.user.git.name() + + } + ); + } + + , writing: + { + createApi: function () + { + var data = this.templateData + , templates = + { + 'api.coffee': [ 'src/apis/' + data.fileBase + '.coffee' ] + } + ; + + this._templatesProcess( templates ); + } + } + } +); + +_.extend( + ApiGenerator.prototype +, require( './../../lib/generator.js' ) +, require( './../../lib/sub-generator.js' ) +); + +module.exports = ApiGenerator; diff --git a/generators/api/templates/api.coffee b/generators/api/templates/api.coffee new file mode 100644 index 00000000..1c1c20ea --- /dev/null +++ b/generators/api/templates/api.coffee @@ -0,0 +1,54 @@ +( ( factory ) -> + if typeof exports is 'object' + module.exports = factory( + + require( './../collections/api-services.coffee' ) + ) + else if typeof define is 'function' and define.amd + define( [ + + './../collections/api-services.coffee' + ], factory ) + return +)(( + + ApiServicesCollection +) -> + + ###* + # @author <%- userName %> + # @module App + # @submodule Apis + ### + + 'use strict' + + + ###*<% if ( description ) { %> + # <%- description %> + #<% } %> + # @class <%- className %> + # @static + ### + + new ApiServicesCollection( + + [ + ] + + , + ###* + # The `<%- className %>`'s base url. + # + # @property url + # @type String + # @final + # + # @default <%- url %> + ### + + url: <%- url %> + + ) + +) diff --git a/lib/youtil.js b/lib/youtil.js index 55323415..421864a0 100644 --- a/lib/youtil.js +++ b/lib/youtil.js @@ -11,6 +11,20 @@ module.exports = return (( value == null ) ? value : '' + value ); } + , isCoffeeScript: function ( value ) + { + try + { + require( 'coffee-script' ).compile( value ); + } + catch ( e ) + { + return false; + } + + return true; + } + , isIdentifier: function ( value ) { return /^[$A-Za-z_\x7f-\uffff][$\w\x7f-\uffff]*$/.test(( '' + value ).trim() ); diff --git a/package.json b/package.json index 57d5b3ca..307adbd0 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ }, "dependencies": { "chalk": "^1.1.3", + "coffee-script": "^1.11.0", "language-tags": "^1.0.5", "lodash": "^4.14.1", "mkdirp": "^0.5.1",