1
0
mirror of https://github.com/JuanCanham/HackMyResume.git synced 2025-05-15 10:07:07 +01:00

Compare commits

..

16 Commits

Author SHA1 Message Date
78a8b9c58e Merge pull request #40 from hacksalot/rel/v1.2.2
rel/v1.2.2
2015-12-24 17:44:56 -05:00
5e7abb66bd Safer source format conversions.
Quick fix against missing fields in FRESH and/or JRS (ahead of introing
more robust standalone converter thing). Address portions of #31 and
#33.
2015-12-24 17:51:26 -05:00
358c397bb9 Show call stack on error.
Hat tip @Furchin.
2015-12-24 16:22:29 -05:00
3d41528059 Fix path parsing issue on prev versions of Node.js.
Work around absence of path.parse in Node versions < v0.12. Addresses
#31 and #33.
2015-12-24 16:18:38 -05:00
79637b611a Bump version to 1.2.2. 2015-12-24 16:09:37 -05:00
5de796b119 Merge pull request #35 from hacksalot/rel/v1.2.1
rel/v1.2.1
2015-12-24 07:41:12 -05:00
bf84341acf Version -> 1.2.1
Holding off on 1.3.0 pending HTTP/HTTPS support. #rebase #semver
2015-12-24 07:42:07 -05:00
bbac1fdceb Improve test coverage around incomplete JRS resumes.
Add quick sanity checks around incomplete or irregular JRS-format
resumes. Also decorate existing JRS test resumes (/tests/resumes/) with
current 0.0.0 JRS version ahead of 1.0.0 release.
2015-12-24 06:08:45 -05:00
c5ee1ee33c Quick fix for ".history" errors.
Affects #31 and #33.
2015-12-24 04:05:56 -05:00
c74eda90ed Introduce lodash dependency. 2015-12-24 04:05:17 -05:00
ef2fe95bd8 Remove unused method. 2015-12-24 04:04:44 -05:00
e2589b3730 Fix validate command error.
Still hitting some inconsistent behavior in different NPM
versions/platforms with invalid uppercase dependency names per
https://github.com/npm/npm/issues/3692. Partial fix for #33.
2015-12-24 03:23:56 -05:00
ebad1677bc Replace file-exists.js with NPM path-exists. 2015-12-22 18:55:17 -05:00
dab6ebfd82 Bump fluent-themes version to 0.8.0-beta. 2015-12-22 18:49:29 -05:00
dd61b5360a Update package.json contact info. 2015-12-22 18:49:11 -05:00
fced92a5a0 Bump version to 1.3.0. 2015-12-22 18:47:23 -05:00
17 changed files with 271 additions and 86 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "hackmyresume", "name": "hackmyresume",
"version": "1.2.0", "version": "1.2.2",
"description": "Generate polished résumés and CVs in HTML, Markdown, LaTeX, MS Word, PDF, plain text, JSON, XML, YAML, smoke signal, and carrier pigeon.", "description": "Generate polished résumés and CVs in HTML, Markdown, LaTeX, MS Word, PDF, plain text, JSON, XML, YAML, smoke signal, and carrier pigeon.",
"repository": { "repository": {
"type": "git", "type": "git",
@ -24,7 +24,7 @@
"Underscore", "Underscore",
"template" "template"
], ],
"author": "hacksalot <hacksalot@fluentdesk.com> (https://github.com/hacksalot)", "author": "hacksalot <hacksalot@indevious.com> (https://github.com/hacksalot)",
"license": "MIT", "license": "MIT",
"preferGlobal": "true", "preferGlobal": "true",
"bugs": { "bugs": {
@ -37,17 +37,20 @@
"homepage": "https://github.com/hacksalot/HackMyResume", "homepage": "https://github.com/hacksalot/HackMyResume",
"dependencies": { "dependencies": {
"colors": "^1.1.2", "colors": "^1.1.2",
"fluent-themes": "~0.7.0-beta", "fluent-themes": "~0.7.1-beta",
"fresca": "~0.2.2", "fresca": "~0.2.2",
"fs-extra": "^0.24.0", "fs-extra": "^0.24.0",
"handlebars": "^4.0.5", "handlebars": "^4.0.5",
"html": "0.0.10", "html": "0.0.10",
"is-my-json-valid": "^2.12.2", "is-my-json-valid": "^2.12.2",
"jst": "0.0.13", "jst": "0.0.13",
"lodash": "^3.10.1",
"marked": "^0.3.5", "marked": "^0.3.5",
"minimist": "^1.2.0", "minimist": "^1.2.0",
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"moment": "^2.10.6", "moment": "^2.10.6",
"parse-filepath": "^0.6.3",
"path-exists": "^2.1.0",
"recursive-readdir-sync": "^1.0.6", "recursive-readdir-sync": "^1.0.6",
"simple-html-tokenizer": "^0.2.0", "simple-html-tokenizer": "^0.2.0",
"underscore": "^1.8.3", "underscore": "^1.8.3",

View File

@ -16,6 +16,7 @@ FRESH to JSON Resume conversion routiens.
/** /**
Convert from JSON Resume format to FRESH. Convert from JSON Resume format to FRESH.
@method toFresh @method toFresh
@todo Refactor
*/ */
toFRESH: function( src, foreign ) { toFRESH: function( src, foreign ) {
@ -67,6 +68,7 @@ FRESH to JSON Resume conversion routiens.
Convert from FRESH format to JSON Resume. Convert from FRESH format to JSON Resume.
@param foreign True if non-JSON-Resume properties should be included in @param foreign True if non-JSON-Resume properties should be included in
the result, false if those properties should be excluded. the result, false if those properties should be excluded.
@todo Refactor
*/ */
toJRS: function( src, foreign ) { toJRS: function( src, foreign ) {
@ -112,6 +114,7 @@ FRESH to JSON Resume conversion routiens.
}; };
function meta( direction, obj ) { function meta( direction, obj ) {
if( !obj ) return obj; // preserve null and undefined
if( direction ) { if( direction ) {
obj = obj || { }; obj = obj || { };
obj.format = obj.format || "FRESH@0.1.0"; obj.format = obj.format || "FRESH@0.1.0";
@ -121,6 +124,7 @@ FRESH to JSON Resume conversion routiens.
} }
function employment( obj, direction ) { function employment( obj, direction ) {
if( !obj ) return obj;
if( !direction ) { if( !direction ) {
return obj && obj.history ? return obj && obj.history ?
obj.history.map(function(emp){ obj.history.map(function(emp){
@ -157,6 +161,7 @@ FRESH to JSON Resume conversion routiens.
function education( obj, direction ) { function education( obj, direction ) {
if( !obj ) return obj;
if( direction ) { if( direction ) {
return obj && obj.length ? { return obj && obj.length ? {
history: obj.map(function(edu){ history: obj.map(function(edu){
@ -191,6 +196,7 @@ FRESH to JSON Resume conversion routiens.
} }
function service( obj, direction, foreign ) { function service( obj, direction, foreign ) {
if( !obj ) return obj;
if( direction ) { if( direction ) {
return { return {
history: obj && obj.length ? obj.map(function(vol) { history: obj && obj.length ? obj.map(function(vol) {
@ -225,6 +231,7 @@ FRESH to JSON Resume conversion routiens.
} }
function social( obj, direction ) { function social( obj, direction ) {
if( !obj ) return obj;
if( direction ) { if( direction ) {
return obj.map(function(pro){ return obj.map(function(pro){
return { return {
@ -247,6 +254,7 @@ FRESH to JSON Resume conversion routiens.
} }
function recognition( obj, direction, foreign ) { function recognition( obj, direction, foreign ) {
if( !obj ) return obj;
if( direction ) { if( direction ) {
return obj && obj.length ? obj.map( return obj && obj.length ? obj.map(
function(awd){ function(awd){
@ -275,6 +283,7 @@ FRESH to JSON Resume conversion routiens.
} }
function references( obj, direction ) { function references( obj, direction ) {
if( !obj ) return obj;
if( direction ) { if( direction ) {
return obj && obj.length && obj.map(function(ref){ return obj && obj.length && obj.map(function(ref){
return { return {
@ -296,6 +305,7 @@ FRESH to JSON Resume conversion routiens.
} }
function writing( obj, direction ) { function writing( obj, direction ) {
if( !obj ) return obj;
if( direction ) { if( direction ) {
return obj.map(function( pub ) { return obj.map(function( pub ) {
return { return {

View File

@ -10,6 +10,7 @@ Definition of the FRESHResume class.
, extend = require('../utils/extend') , extend = require('../utils/extend')
, validator = require('is-my-json-valid') , validator = require('is-my-json-valid')
, _ = require('underscore') , _ = require('underscore')
, __ = require('lodash')
, PATH = require('path') , PATH = require('path')
, moment = require('moment') , moment = require('moment')
, MD = require('marked') , MD = require('marked')
@ -187,15 +188,6 @@ Definition of the FRESHResume class.
return flatSkills; return flatSkills;
}, },
/**
Update the sheet's raw data. TODO: remove/refactor
*/
FreshResume.prototype.updateData = function( str ) {
this.clear( false );
this.parse( str );
return this;
};
/** /**
Reset the sheet to an empty state. Reset the sheet to an empty state.
*/ */
@ -299,7 +291,7 @@ Definition of the FRESHResume class.
Validate the sheet against the FRESH Resume schema. Validate the sheet against the FRESH Resume schema.
*/ */
FreshResume.prototype.isValid = function( info ) { FreshResume.prototype.isValid = function( info ) {
var schemaObj = require('FRESCA'); var schemaObj = require('fresca');
var validator = require('is-my-json-valid'); var validator = require('is-my-json-valid');
var validate = validator( schemaObj, { // Note [1] var validate = validator( schemaObj, { // Note [1]
formats: { date: /^\d{4}(?:-(?:0[0-9]{1}|1[0-2]{1})(?:-[0-9]{2})?)?$/ } formats: { date: /^\d{4}(?:-(?:0[0-9]{1}|1[0-2]{1})(?:-[0-9]{2})?)?$/ }
@ -321,7 +313,8 @@ Definition of the FRESHResume class.
sheets that have overlapping jobs. sheets that have overlapping jobs.
*/ */
FreshResume.prototype.duration = function() { FreshResume.prototype.duration = function() {
if( this.employment.history && this.employment.history.length ) { var empHist = __.get(this, 'employment.history');
if( empHist && empHist.length ) {
var firstJob = _.last( this.employment.history ); var firstJob = _.last( this.employment.history );
var careerStart = firstJob.start ? firstJob.safe.start : ''; var careerStart = firstJob.start ? firstJob.safe.start : '';
if ((typeof careerStart === 'string' || careerStart instanceof String) && if ((typeof careerStart === 'string' || careerStart instanceof String) &&
@ -341,9 +334,9 @@ Definition of the FRESHResume class.
*/ */
FreshResume.prototype.sort = function( ) { FreshResume.prototype.sort = function( ) {
this.employment.history && this.employment.history.sort( byDateDesc ); __.get(this, 'employment.history') && this.employment.history.sort( byDateDesc );
this.education.history && this.education.history.sort( byDateDesc ); __.get(this, 'education.history') && this.education.history.sort( byDateDesc );
this.service.history && this.service.history.sort( byDateDesc ); __.get(this, 'service.history') && this.service.history.sort( byDateDesc );
// this.awards && this.awards.sort( function(a, b) { // this.awards && this.awards.sort( function(a, b) {
// return( a.safeDate.isBefore(b.safeDate) ) ? 1 // return( a.safeDate.isBefore(b.safeDate) ) ? 1

View File

@ -107,15 +107,6 @@ Definition of the JRSResume class.
return flatSkills; return flatSkills;
}; };
/**
Update the sheet's raw data. TODO: remove/refactor
*/
JRSResume.prototype.updateData = function( str ) {
this.clear( false );
this.parse( str );
return this;
};
/** /**
Reset the sheet to an empty state. Reset the sheet to an empty state.
*/ */

View File

@ -11,6 +11,7 @@ Definition of the Theme class.
, validator = require('is-my-json-valid') , validator = require('is-my-json-valid')
, _ = require('underscore') , _ = require('underscore')
, PATH = require('path') , PATH = require('path')
, parsePath = require('parse-filepath')
, EXTEND = require('../utils/extend') , EXTEND = require('../utils/extend')
, moment = require('moment') , moment = require('moment')
, RECURSIVE_READ_DIR = require('recursive-readdir-sync'); , RECURSIVE_READ_DIR = require('recursive-readdir-sync');
@ -30,8 +31,8 @@ Definition of the Theme class.
// Open the [theme-name].json file; should have the same name as folder // Open the [theme-name].json file; should have the same name as folder
this.folder = themeFolder; this.folder = themeFolder;
var pathInfo = PATH.parse( themeFolder ); var pathInfo = parsePath( themeFolder );
var themeFile = PATH.join( themeFolder, pathInfo.base + '.json' ); var themeFile = PATH.join( themeFolder, pathInfo.basename + '.json' );
var themeInfo = JSON.parse( FS.readFileSync( themeFile, 'utf8' ) ); var themeInfo = JSON.parse( FS.readFileSync( themeFile, 'utf8' ) );
var that = this; var that = this;
@ -59,7 +60,7 @@ Definition of the Theme class.
this.formats = formatsHash; this.formats = formatsHash;
// Set the official theme name // Set the official theme name
this.name = PATH.parse( this.folder ).name; this.name = parsePath( this.folder ).name;
return this; return this;
}; };
@ -96,9 +97,9 @@ Definition of the Theme class.
// If this file lives in a specific format folder within the theme, // If this file lives in a specific format folder within the theme,
// such as "/latex" or "/html", then that format is the output format // such as "/latex" or "/html", then that format is the output format
// for all files within the folder. // for all files within the folder.
var pathInfo = PATH.parse(absPath); var pathInfo = parsePath(absPath);
var outFmt = '', isMajor = false; var outFmt = '', isMajor = false;
var portion = pathInfo.dir.replace(tplFolder,''); var portion = pathInfo.dirname.replace(tplFolder,'');
if( portion && portion.trim() ) { if( portion && portion.trim() ) {
if( portion[1] === '_' ) return; if( portion[1] === '_' ) return;
var reg = /^(?:\/|\\)(html|latex|doc|pdf|partials)(?:\/|\\)?/ig; var reg = /^(?:\/|\\)(html|latex|doc|pdf|partials)(?:\/|\\)?/ig;
@ -135,7 +136,7 @@ Definition of the Theme class.
path: absPath, path: absPath,
major: isMajor, major: isMajor,
orgPath: PATH.relative(tplFolder, absPath), orgPath: PATH.relative(tplFolder, absPath),
ext: pathInfo.ext.slice(1), ext: pathInfo.extname.slice(1),
title: friendlyName( outFmt ), title: friendlyName( outFmt ),
pre: outFmt, pre: outFmt,
// outFormat: outFmt || pathInfo.name, // outFormat: outFmt || pathInfo.name,
@ -186,7 +187,7 @@ Definition of the Theme class.
act = null; act = null;
// If this file is mentioned in the theme's JSON file under "transforms" // If this file is mentioned in the theme's JSON file under "transforms"
var pathInfo = PATH.parse(absPath); var pathInfo = parsePath(absPath);
var absPathSafe = absPath.trim().toLowerCase(); var absPathSafe = absPath.trim().toLowerCase();
var outFmt = _.find( Object.keys( that.formats ), function( fmtKey ) { var outFmt = _.find( Object.keys( that.formats ), function( fmtKey ) {
var fmtVal = that.formats[ fmtKey ]; var fmtVal = that.formats[ fmtKey ];
@ -203,7 +204,7 @@ Definition of the Theme class.
// such as "/latex" or "/html", then that format is the output format // such as "/latex" or "/html", then that format is the output format
// for all files within the folder. // for all files within the folder.
if( !outFmt ) { if( !outFmt ) {
var portion = pathInfo.dir.replace(tplFolder,''); var portion = pathInfo.dirname.replace(tplFolder,'');
if( portion && portion.trim() ) { if( portion && portion.trim() ) {
var reg = /^(?:\/|\\)(html|latex|doc|pdf)(?:\/|\\)?/ig; var reg = /^(?:\/|\\)(html|latex|doc|pdf)(?:\/|\\)?/ig;
var res = reg.exec( portion ); var res = reg.exec( portion );
@ -231,7 +232,7 @@ Definition of the Theme class.
action: act, action: act,
orgPath: PATH.relative(that.folder, absPath), orgPath: PATH.relative(that.folder, absPath),
path: absPath, path: absPath,
ext: pathInfo.ext.slice(1), ext: pathInfo.extname.slice(1),
title: friendlyName( outFmt ), title: friendlyName( outFmt ),
pre: outFmt, pre: outFmt,
// outFormat: outFmt || pathInfo.name, // outFormat: outFmt || pathInfo.name,

View File

@ -13,6 +13,7 @@ Definition of the TemplateGenerator class.
, MD = require( 'marked' ) , MD = require( 'marked' )
, XML = require( 'xml-escape' ) , XML = require( 'xml-escape' )
, PATH = require('path') , PATH = require('path')
, parsePath = require('parse-filepath')
, MKDIRP = require('mkdirp') , MKDIRP = require('mkdirp')
, BaseGenerator = require( './base-generator' ) , BaseGenerator = require( './base-generator' )
, EXTEND = require('../utils/extend') , EXTEND = require('../utils/extend')
@ -127,7 +128,7 @@ Definition of the TemplateGenerator class.
var theme = themeInfo.theme; var theme = themeInfo.theme;
var tFolder = themeInfo.folder; var tFolder = themeInfo.folder;
var tplFolder = PATH.join( tFolder, 'src' ); var tplFolder = PATH.join( tFolder, 'src' );
var outFolder = PATH.parse(f).dir; var outFolder = parsePath(f).dirname;
var curFmt = theme.getFormat( this.format ); var curFmt = theme.getFormat( this.format );
var that = this; var that = this;
@ -176,7 +177,7 @@ Definition of the TemplateGenerator class.
var absLoc = PATH.join(outFolder, loc); var absLoc = PATH.join(outFolder, loc);
var absTarg = PATH.join(PATH.dirname(absLoc), curFmt.symLinks[loc]); var absTarg = PATH.join(PATH.dirname(absLoc), curFmt.symLinks[loc]);
// 'file', 'dir', or 'junction' (Windows only) // 'file', 'dir', or 'junction' (Windows only)
var type = PATH.parse( absLoc ).ext ? 'file' : 'junction'; var type = parsePath( absLoc ).extname ? 'file' : 'junction';
FS.symlinkSync( absTarg, absLoc, type); FS.symlinkSync( absTarg, absLoc, type);
}); });
} }
@ -221,10 +222,10 @@ Definition of the TemplateGenerator class.
function themeFromMoniker() { function themeFromMoniker() {
// Verify the specified theme name/path // Verify the specified theme name/path
var tFolder = PATH.join( var tFolder = PATH.join(
PATH.parse( require.resolve('fluent-themes') ).dir, parsePath( require.resolve('fluent-themes') ).dirname,
this.opts.theme this.opts.theme
); );
var exists = require('../utils/file-exists'); var exists = require('path-exists').sync;
if( !exists( tFolder ) ) { if( !exists( tFolder ) ) {
tFolder = PATH.resolve( this.opts.theme ); tFolder = PATH.resolve( this.opts.theme );
if( !exists( tFolder ) ) { if( !exists( tFolder ) ) {

View File

@ -91,6 +91,7 @@ function getOpts( args ) {
}; };
} }
// TODO: refactor
function handleError( ex ) { function handleError( ex ) {
var msg = '', exitCode; var msg = '', exitCode;
@ -123,8 +124,10 @@ function handleError( ex ) {
var idx = msg.indexOf('Error: '); var idx = msg.indexOf('Error: ');
var trimmed = idx === -1 ? msg : msg.substring( idx + 7 ); var trimmed = idx === -1 ? msg : msg.substring( idx + 7 );
if( !ex.fluenterror || ex.fluenterror < 3 ) if( !ex.fluenterror || ex.fluenterror < 3 ) { // TODO: magic #s
console.log( ('ERROR: ' + trimmed.toString()).red.bold ); console.log( ('ERROR: ' + trimmed.toString()).red.bold );
console.log( ex.stack.gray);
}
else else
console.log( trimmed.toString() ); console.log( trimmed.toString() );

View File

@ -1,19 +0,0 @@
/**
Definition of the `fileExists` method.
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
@module file-exists.js
*/
var FS = require('fs');
// Yup, this is now the recommended way to check for file existence on Node.
// fs.exists is deprecated and the recommended fs.statSync/lstatSync throws
// exceptions on non-existent paths :)
module.exports = function (path) {
try {
FS.statSync( path );
return true;
} catch( err ) {
return !(err && err.code === 'ENOENT');
}
};

View File

@ -1,6 +1,7 @@
(function() { (function() {
var PATH = require('path') var PATH = require('path')
, parsePath = require('parse-filepath')
, MKDIRP = require('mkdirp') , MKDIRP = require('mkdirp')
, _opts = require('../core/default-options') , _opts = require('../core/default-options')
, FluentTheme = require('../core/theme') , FluentTheme = require('../core/theme')
@ -51,7 +52,7 @@
// Verify the specified theme name/path // Verify the specified theme name/path
var relativeThemeFolder = '../../node_modules/fluent-themes/themes'; var relativeThemeFolder = '../../node_modules/fluent-themes/themes';
var tFolder = PATH.resolve( __dirname, relativeThemeFolder, _opts.theme); var tFolder = PATH.resolve( __dirname, relativeThemeFolder, _opts.theme);
var exists = require('../utils/file-exists'); var exists = require('path-exists').sync;
if (!exists( tFolder )) { if (!exists( tFolder )) {
tFolder = PATH.resolve( _opts.theme ); tFolder = PATH.resolve( _opts.theme );
if (!exists( tFolder )) { if (!exists( tFolder )) {
@ -60,7 +61,7 @@
} }
// Load the theme // Load the theme
var theTheme = new FluentTheme().open( tFolder ); var theTheme = (new FluentTheme()).open( tFolder );
_opts.themeObj = theTheme; _opts.themeObj = theTheme;
_log( 'Applying '.info + theTheme.name.toUpperCase().infoBold + _log( 'Applying '.info + theTheme.name.toUpperCase().infoBold +
(' theme (' + Object.keys(theTheme.formats).length + ' formats)').info); (' theme (' + Object.keys(theTheme.formats).length + ' formats)').info);
@ -70,8 +71,8 @@
( (dst && dst.length && dst) || ['resume.all'] ).forEach( function(t) { ( (dst && dst.length && dst) || ['resume.all'] ).forEach( function(t) {
var to = PATH.resolve(t), var to = PATH.resolve(t),
pa = PATH.parse(to), pa = parsePath(to),
fmat = pa.ext || '.all'; fmat = pa.extname || '.all';
targets.push.apply(targets, fmat === '.all' ? targets.push.apply(targets, fmat === '.all' ?
Object.keys( theTheme.formats ).map(function(k){ Object.keys( theTheme.formats ).map(function(k){

View File

@ -15,7 +15,7 @@
var validator = require('is-my-json-valid'); var validator = require('is-my-json-valid');
var schemas = { var schemas = {
fresh: require('FRESCA'), fresh: require('fresca'),
jars: require('../core/resume.json') jars: require('../core/resume.json')
}; };

View File

@ -0,0 +1,91 @@
{
"basics": {
"name": "",
"label": "",
"picture": "",
"email": "",
"phone": "",
"degree": "",
"website": "",
"summary": "",
"location": {
"address": "",
"postalCode": "",
"city": "",
"countryCode": "",
"region": ""
},
"profiles": [
{
"network": "",
"username": "",
"url": ""
}
]
},
"work": [
{
"company": "",
"position": "",
"website": "",
"startDate": "",
"endDate": "",
"summary": "",
"highlights": [
""
]
}
],
"awards": [
{
"title": "",
"date": "",
"awarder": "",
"summary": ""
}
],
"education": [
{
"institution": "",
"area": "",
"studyType": "",
"startDate": "",
"endDate": "",
"gpa": "",
"courses": [
""
]
}
],
"publications": [
{
"name": "",
"publisher": "",
"releaseDate": "",
"website": "",
"summary": ""
}
],
"volunteer": [
{
"organization": "",
"position": "",
"website": "",
"startDate": "",
"endDate": "",
"summary": "",
"highlights": [
""
]
}
],
"skills": [
{
"name": "",
"level": "",
"keywords": [
""
]
}
]
}

View File

@ -0,0 +1,104 @@
{
"basics": {
"name": "Jane Q. Fullstacker"
},
"education": [
{
"gpa": "3.5",
"courses": [
"Course 1",
"Course 2",
"Course 2"
],
"startDate": "2005-09",
"endDate": "2008-05"
},
{
"institution": "Medfield College",
"gpa": "3.2",
"courses": [
"Course 1",
"Course 2",
"Course 2"
],
"startDate": "2003-09",
"endDate": "2005-06"
}
],
"skills": [
{
"name": "Web Dev",
"keywords": [
"JavaScript",
"HTML 5",
"CSS",
"LAMP",
"MVC",
"REST"
]
},
{
"name": "JavaScript"
}
],
"volunteer": [],
"publications": [
{
"name": "Building User Interfaces with Electron and Atom",
"releaseDate": "2011",
"website": "http://codeproject.com/build-ui-electron-atom.aspx"
},
{
"name": "Jane Fullstacker's Blog",
"publisher": "self",
"releaseDate": "2011",
"website": "http://janef.me"
},
{
"name": "Teach Yourself GORFF in 21 Days",
"publisher": "Amazon"
}
],
"interests": [
{
"name": "reading",
"summary": "Jane is a fan of mystery novels and courtroom dramas including Agatha Christie and John Grisham.",
"keywords": [
"mystery",
"Agatha Christie",
"John Grisham"
]
},
{
"name": "hiking",
"summary": "Jane enjoys hiking, light mountain climbing, and has four summits under her belt!"
},
{
"name": "yoga"
}
],
"references": [
{
"name": "John Davidson",
"reference": "Jane is awesome! I'd hire her again in a heartbeat."
},
{
"name": "Elias Fullstacker",
"reference": "I worked with Jane on Jabberwocky and can vouch for her awesome technical capabilities and attention to detail. Insta-hire."
},
{
"name": "Dana Nevins",
"reference": "I've known Jane personally and professionally for almost ten years. She is one in a million."
}
],
"languages": [
{
"language": "English",
"level": "Native"
},
{
"language": "Spanish",
"level": "Moderate"
}
]
}

View File

@ -18,7 +18,7 @@ describe('FRESH/JRS converter', function () {
it('should round-trip from JRS to FRESH to JRS without modifying or losing data', function () { it('should round-trip from JRS to FRESH to JRS without modifying or losing data', function () {
var fileA = path.join( __dirname, 'resumes/jrs/richard-hendriks.json' ); var fileA = path.join( __dirname, 'resumes/jrs-0.0.0/richard-hendriks.json' );
var fileB = path.join( __dirname, 'sandbox/richard-hendriks.json' ); var fileB = path.join( __dirname, 'sandbox/richard-hendriks.json' );
_sheet = new FRESHResume().open( fileA ); _sheet = new FRESHResume().open( fileA );

View File

@ -55,7 +55,7 @@ describe('jane-doe.json (FRESH)', function () {
it('should validate against the FRESH resume schema', function() { it('should validate against the FRESH resume schema', function() {
var result = _sheet.isValid(); var result = _sheet.isValid();
// var schemaJson = require('FRESCA'); // var schemaJson = require('fresca');
// var validate = validator( schemaJson, { verbose: true } ); // var validate = validator( schemaJson, { verbose: true } );
// var result = validate( JSON.parse( _sheet.imp.raw ) ); // var result = validate( JSON.parse( _sheet.imp.raw ) );
result || console.log("\n\nOops, resume didn't validate. " + result || console.log("\n\nOops, resume didn't validate. " +

View File

@ -9,55 +9,61 @@ var chai = require('chai')
chai.config.includeStack = false; chai.config.includeStack = false;
describe('jane-doe.json (JRS)', function () { function testResume( opts ) {
describe( opts.title + ' (JRS)', function() {
opts.isValid = opts.isValid !== false;
var _sheet; var _sheet;
it('should open without throwing an exception', function () { it('should open without throwing an exception', function () {
var that = this;
function tryOpen() { function tryOpen() {
_sheet = new JRSResume().open( _sheet = new JRSResume().open(
path.join( __dirname, 'resumes/jrs/jane-q-fullstacker.json' ) ); path.join( __dirname, 'resumes/jrs-0.0.0/' + opts.title + '.json' ) );
} }
tryOpen.should.not.Throw(); tryOpen.should.not.Throw();
}); });
it('should have one or more of each section', function() { it('should have one or more of each section', function() {
expect( var newObj = _.pick( _sheet, opts.sections );
(_sheet.basics) && expect( Object.keys(newObj).length ).to.equal( opts.sections.length );
(_sheet.work && _sheet.work.length > 0) &&
(_sheet.skills && _sheet.skills.length > 0) &&
(_sheet.education && _sheet.education.length > 0) &&
(_sheet.volunteer && _sheet.volunteer.length > 0) &&
(_sheet.publications && _sheet.publications.length > 0) &&
(_sheet.awards && _sheet.awards.length > 0)
).to.equal( true );
}); });
it('should have a work duration of 7 years', function() { it('should have a work duration of ' + opts.duration + ' years', function() {
_sheet.basics.computed.numYears.should.equal( 7 ); _sheet.basics.computed.numYears.should.equal( opts.duration );
}); });
it('should save without throwing an exception', function() { it('should save without throwing an exception', function() {
var that = this;
function trySave() { function trySave() {
_sheet.save( 'tests/sandbox/jane-q-fullstacker.json' ); _sheet.save( 'tests/sandbox/' + opts.title + '.json' );
} }
trySave.should.not.Throw(); trySave.should.not.Throw();
}); });
it('should not be modified after saving', function() { it('should not be modified after saving', function() {
var savedSheet = new JRSResume().open( 'tests/sandbox/jane-q-fullstacker.json' ); var savedSheet = new JRSResume().open( 'tests/sandbox/' + opts.title + '.json' );
_sheet.stringify().should.equal( savedSheet.stringify() ) _sheet.stringify().should.equal( savedSheet.stringify() )
}); });
it('should validate against the JSON Resume schema', function() { it('should ' + (opts.isValid ? '' : 'NOT ') + 'validate against the JSON Resume schema', function() {
var result = _sheet.isValid(); var result = _sheet.isValid();
// var schemaJson = require('../src/core/resume.json'); // var schemaJson = require('../src/core/resume.json');
// var validate = validator( schemaJson, { verbose: true } ); // var validate = validator( schemaJson, { verbose: true } );
// var result = validate( JSON.parse( _sheet.imp.raw ) ); // var result = validate( JSON.parse( _sheet.imp.raw ) );
result || console.log("\n\nOops, resume didn't validate. " + result || console.log("\n\nOops, resume didn't validate. " +
"Validation errors:\n\n", _sheet.basics.imp.validationErrors, "\n\n"); "Validation errors:\n\n", _sheet.basics.imp.validationErrors, "\n\n");
result.should.equal( true ); result.should.equal( opts.isValid );
}); });
}); });
}
var sects = [ 'basics', 'work', 'volunteer', 'skills', 'education', 'publications', 'awards', 'references' ];
testResume({ title: 'jane-q-fullstacker', duration: 7, sections: sects });
testResume({ title: 'jane-incomplete', duration: 0, sections: _.without(sects, 'awards', 'work') });
testResume({ title: 'richard-hendriks', duration: 1, sections: sects });
testResume({ title: 'empty', duration: 0, sections: [], isValid: false });