1
0
mirror of https://github.com/JuanCanham/HackMyResume.git synced 2024-11-22 16:30:11 +00:00
This commit is contained in:
hacksalot 2016-01-10 14:53:22 -05:00
parent a4ee7127ee
commit 656dbe2fc2
8 changed files with 146 additions and 101 deletions

View File

@ -52,6 +52,10 @@ Output routines for HackMyResume.
chalk.green(' resume: ') + chalk.green.bold(evt.file)); chalk.green(' resume: ') + chalk.green.bold(evt.file));
break; break;
case HME.beforeRead:
this.log( chalk.cyan('Reading resume: ' + chalk.bold( evt.file )));
break;
case HME.afterTheme: case HME.afterTheme:
this.theme = evt.theme; this.theme = evt.theme;
break; break;
@ -148,6 +152,23 @@ Output routines for HackMyResume.
chalk.green(' (' + evt.srcFmt + ') to ') + chalk.green.bold(evt.dstFile) + chalk.green(' (' + evt.srcFmt + ') to ') + chalk.green.bold(evt.dstFile) +
chalk.green(' (' + evt.dstFmt + ').')); chalk.green(' (' + evt.dstFmt + ').'));
break; break;
case HME.afterValidate:
var style = evt.isValid ? 'green' : 'yellow';
this.log( chalk.white('Validating ') + chalk.white.bold(evt.file) + chalk.white(' against ') +
chalk.white.bold( evt.fmt ).toUpperCase() +
chalk.white(' schema: ') + chalk[style].bold(evt.isValid ? 'VALID!' : 'INVALID'));
if( evt.errors ) {
_.each(evt.errors, function(err,idx) {
this.log( chalk.yellow.bold('--> ') +
chalk.yellow(err.field.replace('data.','resume.').toUpperCase() + ' ' +
err.message) );
}, this);
}
break;
} }
} }

View File

@ -2,26 +2,63 @@ Usage:
hackmyresume <COMMAND> <SOURCES> [TO <TARGETS>] [-t <THEME>] [-f <FORMAT>] hackmyresume <COMMAND> <SOURCES> [TO <TARGETS>] [-t <THEME>] [-f <FORMAT>]
<COMMAND> should be BUILD, NEW, CONVERT, VALIDATE, ANALYZE or HELP. <SOURCES> <COMMAND> should be BUILD, ANALYZE, NEW, CONVERT, or VALIDATE. <SOURCES> should
should be the path to one or more FRESH or JSON Resume format resumes. <TARGETS> be the path to one or more FRESH or JSON Resume format resumes. <TARGETS> should
should be the name of the destination resume to be created, if any. The <THEME> be the name of the destination resume to be created, if any. The <THEME>
parameter should be the name of a predefined theme (for example: COMPACT, parameter should be the name of a predefined theme (for example: COMPACT,
MINIMIST, MODERN, or HELLO-WORLD) or the relative path to a custom theme. MINIMIST, MODERN, or HELLO-WORLD) or the relative path to a custom theme.
<FORMAT> should be either FRESH (for a FRESH-format resume) or JRS (for a JSON <FORMAT> should be either FRESH (for a FRESH-format resume) or JRS (for a JSON
Resume-format resume). Resume-format resume).
hackmyresume BUILD resume.json TO out/resume.all hackmyresume BUILD resume.json TO out/resume.all
hackmyresume NEW resume.json hackmyresume ANALYZE resume.json
hackmyresume CONVERT resume.json TO resume-jrs.json hackmyresume NEW resume.json
hackmyresume ANALYZE resume.json hackmyresume CONVERT resume.json TO resume-jrs.json
hackmyresume VALIDATE resume.json hackmyresume VALIDATE resume.json
Both SOURCES and TARGETS can accept multiple files: Both SOURCES and TARGETS can accept multiple files:
hackmyresume BUILD r1.json r2.json TO out/resume.all out2/resume.html hackmyresume BUILD r1.json r2.json TO out/resume.all out2/resume.html
hackmyresume NEW r1.json r2.json r3.json hackmyresume ANALYZE r1.json r2.json r3.json
hackmyresume ANALYZE r1.json r2.json r3.json hackmyresume NEW r1.json r2.json r3.json
hackmyresume VALIDATE resume.json resume2.json resume3.json hackmyresume CONVERT r1.json r2.json TO o1.json o2.json
hackmyresume VALIDATE r1.json r2.json r3.json
Available options:
--theme <theme> Specify a theme for the BUILD command. Can be the path
-t <theme> to any FRESH or JSON Resume theme, or the name of one
of HackMyResume's predefined themes.
--pdf <engine> Specify a PDF rendering engine. Can be "wkhtmltopdf",
-p <engine> "phantom", or "none". Requires that the corresponding
engine be installed and path-accessible.
--opts <path> Load HackMyResume options via an external file.
-o <path>
--color Enable/disable terminal colors.
--no-color
--tips Enable/disable theme tips and messages.
--no-tips
--debug Emit debug info.
-d
--help Display help documentation.
-h
--version Display the current HackMyResume version.
-v
See https://github.com/hacksalot/hackmyresume/blob/master/README.md for more See https://github.com/hacksalot/hackmyresume/blob/master/README.md for more
information. information.

View File

@ -156,6 +156,21 @@ Error-handling routines for HackMyResume.
" is inaccessible. PDF not generated." ); " is inaccessible. PDF not generated." );
break; break;
case HACKMYSTATUS.readError:
msg = formatError( ex.inner.toString() );
break;
case HACKMYSTATUS.parseError:
if( SyntaxErrorEx.is( ex.inner )) {
var se = new SyntaxErrorEx( ex, ex.raw );
msg = formatError( 'Invalid or corrupt JSON on line ' + se.line +
' column ' + se.col );
}
else {
msg = formatError( ex.inner.toString() );
}
break;
} }
return { return {
msg: msg, msg: msg,

View File

@ -40,7 +40,12 @@ Event code definitions.
afterAnalyze: 14, afterAnalyze: 14,
beforeConvert: 15, beforeConvert: 15,
afterConvert: 16 afterConvert: 16,
verifyOutputs: 17,
beforeParse: 18,
afterParse: 19
}; };

View File

@ -10,11 +10,14 @@ Definition of the ResumeFactory class.
require('string.prototype.startswith'); var FS = require('fs'),
var FS = require('fs'); HACKMYSTATUS = require('./status-codes'),
var ResumeConverter = require('./convert'); HME = require('./event-codes'),
var chalk = require('chalk'); ResumeConverter = require('./convert'),
var SyntaxErrorEx = require('../utils/syntax-error-ex'); chalk = require('chalk'),
SyntaxErrorEx = require('../utils/syntax-error-ex'),
_ = require('underscore');
require('string.prototype.startswith');
@ -29,14 +32,11 @@ Definition of the ResumeFactory class.
/** /**
Load one or more resumes from disk. Load one or more resumes from disk.
*/ */
load: function ( sources, opts ) { load: function ( sources, opts, emitter ) {
// Loop over all inputs, parsing each to JSON and then to a FRESHResume
// or JRSResume object.
var that = this;
return sources.map( function( src ) { return sources.map( function( src ) {
return that.loadOne( src, opts ); return this.loadOne( src, opts, emitter );
}); }, this);
}, },
@ -45,9 +45,8 @@ Definition of the ResumeFactory class.
/** /**
Load a single resume from disk. Load a single resume from disk.
*/ */
loadOne: function( src, opts ) { loadOne: function( src, opts, emitter ) {
var log = opts.log;
var toFormat = opts.format; var toFormat = opts.format;
var objectify = opts.objectify; var objectify = opts.objectify;
@ -55,7 +54,7 @@ Definition of the ResumeFactory class.
toFormat && (toFormat = toFormat.toLowerCase().trim()); toFormat && (toFormat = toFormat.toLowerCase().trim());
// Load and parse the resume JSON // Load and parse the resume JSON
var info = _parse( src, opts ); var info = _parse( src, opts, emitter );
if( info.error ) return info; if( info.error ) return info;
// Determine the resume format: FRESH or JRS // Determine the resume format: FRESH or JRS
@ -88,44 +87,28 @@ Definition of the ResumeFactory class.
function _parse( fileName, opts ) { function _parse( fileName, opts, eve ) {
var rawData; var rawData;
try { try {
// TODO: Core should not log eve && eve.stat( HME.beforeRead, { file: fileName });
opts.log( chalk.cyan('Reading resume: ') + chalk.cyan.bold(fileName) );
// Read the file
rawData = FS.readFileSync( fileName, 'utf8' ); rawData = FS.readFileSync( fileName, 'utf8' );
eve && eve.stat( HME.afterRead, { data: rawData });
// Parse it to JSON eve && eve.stat( HME.beforeParse, { data: rawData });
return { var ret = {
json: JSON.parse( rawData ) json: JSON.parse( rawData )
}; };
eve && eve.stat( HME.afterParse, { data: ret.json } );
return ret;
} }
catch( ex ) { catch( ex ) {
throw {
// JSON.parse failed due to invalid JSON fluenterror: rawData ? HACKMYSTATUS.parseError : HACKMYSTATUS.readError,
if ( !opts.muffle && ex instanceof SyntaxError) { inner: ex, raw: rawData, file: fileName, shouldExit: false
var info = new SyntaxErrorEx( ex, rawData );
opts.log( chalk.red.bold(fileName.toUpperCase() + ' contains invalid JSON on line ' +
info.line + ' column ' + info.col + '.' +
chalk.red(' Unable to validate.')));
opts.log( chalk.red.bold('INTERNAL: ' + ex) );
ex.handled = true;
}
// FS.readFileSync failed
if( !rawData || opts.throw ) throw ex;
return {
error: ex,
raw: rawData,
file: fileName
}; };
} }
} }

View File

@ -20,7 +20,9 @@ Status codes for HackMyResume.
missingPackageJSON: 10, missingPackageJSON: 10,
invalid: 11, invalid: 11,
invalidFormat: 12, invalidFormat: 12,
notOnPath: 13 notOnPath: 13,
readError: 14,
parseError: 15
}; };
}()); }());

View File

@ -31,6 +31,7 @@ Implementation of the 'generate' verb for HackMyResume.
, _err, _log, rez; , _err, _log, rez;
var BuildVerb = module.exports = Verb.extend({ var BuildVerb = module.exports = Verb.extend({
init: function() { init: function() {
@ -64,7 +65,7 @@ Implementation of the 'generate' verb for HackMyResume.
this.stat( HME.afterTheme, { theme: theme }); this.stat( HME.afterTheme, { theme: theme });
// Check for invalid outputs // Check for invalid outputs
var inv = verifyOutputs( dst, theme ); var inv = verifyOutputs.call( this, dst, theme );
if( inv && inv.length ) { if( inv && inv.length ) {
throw {fluenterror: HACKMYSTATUS.invalidFormat, data: inv, theme: theme}; throw {fluenterror: HACKMYSTATUS.invalidFormat, data: inv, theme: theme};
} }
@ -181,6 +182,8 @@ Implementation of the 'generate' verb for HackMyResume.
*/ */
function verifyOutputs( targets, theme ) { function verifyOutputs( targets, theme ) {
this.stat(HME.verifyOutputs, { targets: targets, theme: theme });
return _.reject( return _.reject(
targets.map( function( t ) { targets.map( function( t ) {
var pathInfo = parsePath( t ); var pathInfo = parsePath( t );

View File

@ -12,6 +12,8 @@ Implementation of the 'validate' verb for HackMyResume.
var chalk = require('chalk'); var chalk = require('chalk');
var Verb = require('../core/verb'); var Verb = require('../core/verb');
var HACKMYSTATUS = require('../core/status-codes'); var HACKMYSTATUS = require('../core/status-codes');
var HME = require('../core/event-codes');
var _ = require('underscore');
@ -45,74 +47,51 @@ Implementation of the 'validate' verb for HackMyResume.
var resumes = ResumeFactory.load( sources, { var resumes = ResumeFactory.load( sources, {
format: null, format: null,
objectify: false, objectify: false
throw: false, }, this );
muffle: true
});
// Load input resumes... // Validate input resumes. Return a { file: <f>, isValid: <v>} object for
resumes.forEach(function( src ) { // each resume (valid, invalid, or broken).
return resumes.map( function( src ) {
var ret = { file: src, isValid: false };
// If there was an error reading the resume
if( src.error ) { if( src.error ) {
// TODO: Core should not log
_log( chalk.white('Validating ') + chalk.gray.bold(src.file) +
chalk.white(' against ') + chalk.gray.bold('AUTO') +
chalk.white(' schema:') + chalk.red.bold(' BROKEN') );
var ex = src.error; // alias
if ( ex instanceof SyntaxError) {
var info = new SyntaxErrorEx( ex, src.raw );
_log( chalk.red.bold('--> ' + src.file.toUpperCase() + ' contains invalid JSON on line ' +
info.line + ' column ' + info.col + '.' +
chalk.red(' Unable to validate.') ) );
_log( chalk.red.bold(' INTERNAL: ' + ex) );
}
else {
_log(chalk.red.bold('ERROR: ' + ex.toString()));
}
if( opts.assert ) throw { fluenterror: HACKMYSTATUS.invalid }; if( opts.assert ) throw { fluenterror: HACKMYSTATUS.invalid };
return; return ret;
} }
var json = src.json; // Successfully read the resume. Now parse it as JSON.
var isValid = false; var json = src.json, fmt = json.basics ? 'jrs' : 'fresh', errors = [];
var style = 'green';
var errors = [];
var fmt = json.basics ? 'jrs' : 'fresh';
try { try {
var validate = validator( schemas[ fmt ], { // Note [1] var validate = validator( schemas[ fmt ], { // Note [1]
formats: { formats: {
date: /^\d{4}(?:-(?:0[0-9]{1}|1[0-2]{1})(?:-[0-9]{2})?)?$/ date: /^\d{4}(?:-(?:0[0-9]{1}|1[0-2]{1})(?:-[0-9]{2})?)?$/
} }
}); });
isValid = validate( json ); ret.isValid = validate( json );
if( !isValid ) { if( !ret.isValid ) {
style = 'yellow';
errors = validate.errors; errors = validate.errors;
} }
} }
catch(exc) { catch(exc) {
return; return ret;
} }
_log( chalk.white('Validating ') + chalk.white.bold(src.file) + chalk.white(' against ') + this.stat(HME.afterValidate, { file: src.file, isValid: isValid,
chalk.white.bold(fmt.replace('jars','JSON Resume').toUpperCase()) + fmt: fmt.replace('jars', 'JSON Resume'), errors: errors });
chalk.white(' schema: ') + chalk[style].bold(isValid ? 'VALID!' : 'INVALID') );
errors.forEach(function(err,idx) {
_log( chalk.yellow.bold('--> ') +
chalk.yellow(err.field.replace('data.','resume.').toUpperCase() + ' ' +
err.message) );
});
if( opts.assert && !isValid ) { if( opts.assert && !isValid ) {
throw { fluenterror: HACKMYSTATUS.invalid, shouldExit: true }; throw { fluenterror: HACKMYSTATUS.invalid, shouldExit: true };
} }
}); return ret;
}, this);
} }
}()); }());