From a410153253780e2056e24b700ba05b6fd863aadb Mon Sep 17 00:00:00 2001 From: devlinjd Date: Thu, 19 Nov 2015 09:46:02 -0500 Subject: [PATCH] Implement "generate" and "validate" verbs. Start moving to a more familiar verb-based interface with "generate" and "validate" commands. Use with "fluentcv generate" or "fluentcv validate". --- src/core/fresh-sheet.js | 118 ++++---------------------------------- src/fluentcmd.js | 51 +++++++++++++++- src/fluentlib.js | 2 +- src/gen/base-generator.js | 4 +- src/index.js | 28 +++++---- 5 files changed, 83 insertions(+), 120 deletions(-) diff --git a/src/core/fresh-sheet.js b/src/core/fresh-sheet.js index ce72b45..ac80135 100644 --- a/src/core/fresh-sheet.js +++ b/src/core/fresh-sheet.js @@ -10,7 +10,8 @@ FRESH character/resume sheet representation. , validator = require('is-my-json-valid') , _ = require('underscore') , PATH = require('path') - , moment = require('moment'); + , moment = require('moment') + , CONVERTER = require('./convert'); /** A FRESH-style resume in JSON or YAML. @@ -66,7 +67,7 @@ FRESH character/resume sheet representation. var rep = JSON.parse( stringData ); // Convert JSON Resume to FRESH if necessary - rep.basics && (rep = FreshSheet.convert( rep )); + rep.basics && (rep = CONVERTER.toFRESH( rep )); // Now apply the resume representation onto this object extend( true, this, rep ); @@ -87,106 +88,6 @@ FRESH character/resume sheet representation. return this; }; - /** - Convert from JSON Resume format - */ - FreshSheet.convert = function( jrs ) { - - return { - - name: jrs.basics.name, - label: jrs.basics.label, - class: jrs.basics.label, - summary: jrs.basics.summary, - - contact: { - email: jrs.basics.email, - phone: jrs.basics.phone, - website: jrs.basics.website, - postal: { - city: jrs.basics.location.city, - region: jrs.basics.location.region, - country: jrs.basics.location.countryCode, - code: jrs.basics.location.postalCode, - address: [ - jrs.basics.location.address, - ] - } - }, - - employment: { - history: jrs.work.map( function( job ) { - return { - position: job.position, - employer: job.company, - summary: job.summary, - current: !job.endDate || !job.endDate.trim() || job.endDate.trim().toLowerCase() === 'current', - start: job.startDate, - end: job.endDate, - url: job.website, - keywords: "", - highlights: job.highlights - }; - }) - }, - - education: { - history: jrs.education.map(function(edu){ - return { - institution: edu.institution, - start: edu.startDate, - end: edu.endDate, - grade: edu.gpa, - curriculum: edu.courses, - url: edu.website || edu.url || null, - summary: null, - // ???: edu.area, TODO - // ???: edu.studyType TODO - }; - }) - }, - - service: { - history: jrs.volunteer.map(function(vol) { - return { - type: 'volunteer', - position: vol.position, - organization: vol.organization, - start: vol.startDate, - end: vol.endDate, - url: vol.website, - summary: vol.summary, - highlights: vol.highlights - }; - }) - }, - - skills: jrs.skills.map(function(sk){ - return { - name: sk.name, - summary: "", - level: sk.level, - summary: sk.keywords.join(', '), - years: null, - proof: null - }; - }), - - publications: jrs.publications.map(function(pub){ - return { - title: pub.name, - publisher: pub.publisher, - link: [ - { 'url': pub.website } - ], - year: pub.releaseDate - }; - }), - - interests: jrs.interests - }; - }; - /** Return a unique list of all keywords across all skills. */ @@ -266,12 +167,17 @@ FRESH character/resume sheet representation. /** Validate the sheet against the FRESH Resume schema. */ - FreshSheet.prototype.isValid = function( ) { // TODO: ↓ fix this path ↓ - var schema = FS.readFileSync( PATH.join( __dirname, 'resume.json' ), 'utf8' ); - var schemaObj = JSON.parse( schema ); + FreshSheet.prototype.isValid = function( info ) { + var schemaObj = require('FRESCA'); + //var schemaObj = JSON.parse( schema ); var validator = require('is-my-json-valid') var validate = validator( schemaObj ); - return validate( this ); + var ret = validate( this ); + if( !ret ) { + this.meta = this.meta || { }; + this.meta.validationErrors = validate.errors; + } + return ret; }; /** diff --git a/src/fluentcmd.js b/src/fluentcmd.js index 1fedabc..29f7455 100644 --- a/src/fluentcmd.js +++ b/src/fluentcmd.js @@ -9,7 +9,7 @@ module.exports = function () { var path = require( 'path' ) , extend = require( './utils/extend' ) , unused = require('./utils/string') - , fs = require('fs') + , FS = require('fs') , _ = require('underscore') , FLUENT = require('./fluentlib') , PATH = require('path') @@ -109,6 +109,46 @@ module.exports = function () { throw ex; } + /** + Validate 1 to N resumes as vanilla JSON. + */ + // function validateAsJSON( src, logger ) { + // _log = logger || console.log; + // if( !src || !src.length ) { throw { fluenterror: 3 }; } + // var isValid = true; + // var sheets = src.map( function( res ) { + // try { + // var rawJson = FS.readFileSync( res, 'utf8' ); + // var testObj = JSON.parse( rawJson ); + // } + // catch(ex) { + // if (!(ex instanceof SyntaxError)) { throw ex; } // [1] + // isValid = false; + // } + // + // _log( 'Validating JSON resume: ' + res + (isValid ? ' (VALID)' : ' (INVALID)')); + // return isValid; + // }); + // } + + /** + Validate 1 to N resumes in either FRESH or JSON Resume format. + */ + function validate( src, unused, opts, logger ) { + _log = logger || console.log; + if( !src || !src.length ) { throw { fluenterror: 3 }; } + var isValid = true; + var sheets = src.map( function( res ) { + var sheet = (new FLUENT.Sheet()).open( res ); + var valid = sheet.isValid(); + _log( 'Validating JSON resume: ' + res + + (valid ? ' (VALID)' : ' (INVALID)')); + if( !valid ) { + _log( sheet.meta.validationErrors ); + } + }); + } + /** Supported resume formats. */ @@ -139,10 +179,17 @@ module.exports = function () { Internal module interface. Used by FCV Desktop and HMR. */ return { - generate: gen, + verbs: { + generate: gen, + validate: validate, + convert: convert + }, lib: require('./fluentlib'), options: _opts, formats: _fmts }; }(); + +// [1]: JSON.parse throws SyntaxError on invalid JSON. See: +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse diff --git a/src/fluentlib.js b/src/fluentlib.js index 1476c24..226b693 100644 --- a/src/fluentlib.js +++ b/src/fluentlib.js @@ -4,7 +4,7 @@ External API surface for FluentCV:CLI. */ module.exports = { - Sheet: require('./core/sheet'), + Sheet: require('./core/fresh-sheet'), Theme: require('./core/theme'), FluentDate: require('./core/fluent-date'), HtmlGenerator: require('./gen/html-generator'), diff --git a/src/gen/base-generator.js b/src/gen/base-generator.js index 0a239f1..86bc452 100644 --- a/src/gen/base-generator.js +++ b/src/gen/base-generator.js @@ -29,7 +29,9 @@ Base resume generator for FluentCV. success: 0, themeNotFound: 1, copyCss: 2, - resumeNotFound: 3 + resumeNotFound: 3, + missingCommand: 4, + invalidCommand: 5 }, /** diff --git a/src/index.js b/src/index.js index 23d9ad3..b528898 100644 --- a/src/index.js +++ b/src/index.js @@ -23,20 +23,27 @@ catch( ex ) { function main() { - // Setup. + // Setup var title = '*** FluentCV v' + PKG.version + ' ***'; - if( process.argv.length <= 2 ) { logMsg(title); throw { fluenterror: 3 }; } - var args = ARGS( process.argv.slice(2) ); - opts = getOpts( args ); + if( process.argv.length <= 2 ) { logMsg(title); throw { fluenterror: 4 }; } + var a = ARGS( process.argv.slice(2) ); + opts = getOpts( a ); logMsg( title ); - // Convert arguments to source files, target files, options - var src = args._ || []; - var dst = (args.o && ((typeof args.o === 'string' && [ args.o ]) || args.o)) || []; - dst = (dst === true) ? [] : dst; // Handle -o with missing output file + // Get the action to be performed + var verb = a._[0].toLowerCase().trim(); + if( !FCMD.verbs[ verb ] ) { + throw 'Invalid command: "' + verb + '"'; + } + + // Preload our params array + var dst = (a.o && ((typeof a.o === 'string' && [ a.o ]) || a.o)) || []; + dst = (dst === true) ? [] : dst; // Handle -o with missing output file + var parms = [ a._.slice(1) || [], dst, opts, logMsg ]; + + // Invoke the action + FCMD.verbs[ verb ].apply( null, parms ); - // Generate! - FCMD.generate( src, dst, opts, logMsg ); } function logMsg( msg ) { @@ -60,6 +67,7 @@ function handleError( ex ) { case 1: msg = "The specified theme couldn't be found: " + ex.data; break; case 2: msg = "Couldn't copy CSS file to destination folder"; break; case 3: msg = "Please specify a valid JSON resume file."; break; + case 4: msg = "Please specify a valid command (GENERATE, VALIDATE, or CONVERT)." }; exitCode = ex.fluenterror; }