diff --git a/package.json b/package.json index d3e0751..76b42a0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hackmyresume", - "version": "1.2.0", + "version": "1.2.1", "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": { "type": "git", @@ -24,7 +24,7 @@ "Underscore", "template" ], - "author": "hacksalot (https://github.com/hacksalot)", + "author": "hacksalot (https://github.com/hacksalot)", "license": "MIT", "preferGlobal": "true", "bugs": { @@ -37,17 +37,19 @@ "homepage": "https://github.com/hacksalot/HackMyResume", "dependencies": { "colors": "^1.1.2", - "fluent-themes": "~0.7.0-beta", + "fluent-themes": "~0.7.1-beta", "fresca": "~0.2.2", "fs-extra": "^0.24.0", "handlebars": "^4.0.5", "html": "0.0.10", "is-my-json-valid": "^2.12.2", "jst": "0.0.13", + "lodash": "^3.10.1", "marked": "^0.3.5", "minimist": "^1.2.0", "mkdirp": "^0.5.1", "moment": "^2.10.6", + "path-exists": "^2.1.0", "recursive-readdir-sync": "^1.0.6", "simple-html-tokenizer": "^0.2.0", "underscore": "^1.8.3", diff --git a/src/core/fresh-resume.js b/src/core/fresh-resume.js index 261b1a8..4754194 100644 --- a/src/core/fresh-resume.js +++ b/src/core/fresh-resume.js @@ -10,6 +10,7 @@ Definition of the FRESHResume class. , extend = require('../utils/extend') , validator = require('is-my-json-valid') , _ = require('underscore') + , __ = require('lodash') , PATH = require('path') , moment = require('moment') , MD = require('marked') @@ -187,15 +188,6 @@ Definition of the FRESHResume class. 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. */ @@ -299,7 +291,7 @@ Definition of the FRESHResume class. Validate the sheet against the FRESH Resume schema. */ FreshResume.prototype.isValid = function( info ) { - var schemaObj = require('FRESCA'); + var schemaObj = require('fresca'); var validator = require('is-my-json-valid'); var validate = validator( schemaObj, { // Note [1] 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. */ 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 careerStart = firstJob.start ? firstJob.safe.start : ''; if ((typeof careerStart === 'string' || careerStart instanceof String) && @@ -341,9 +334,9 @@ Definition of the FRESHResume class. */ FreshResume.prototype.sort = function( ) { - this.employment.history && this.employment.history.sort( byDateDesc ); - this.education.history && this.education.history.sort( byDateDesc ); - this.service.history && this.service.history.sort( byDateDesc ); + __.get(this, 'employment.history') && this.employment.history.sort( byDateDesc ); + __.get(this, 'education.history') && this.education.history.sort( byDateDesc ); + __.get(this, 'service.history') && this.service.history.sort( byDateDesc ); // this.awards && this.awards.sort( function(a, b) { // return( a.safeDate.isBefore(b.safeDate) ) ? 1 diff --git a/src/core/jrs-resume.js b/src/core/jrs-resume.js index 9a5bbdd..c9311a0 100644 --- a/src/core/jrs-resume.js +++ b/src/core/jrs-resume.js @@ -107,15 +107,6 @@ Definition of the JRSResume class. 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. */ diff --git a/src/gen/template-generator.js b/src/gen/template-generator.js index e12eef0..4510ade 100644 --- a/src/gen/template-generator.js +++ b/src/gen/template-generator.js @@ -224,7 +224,7 @@ Definition of the TemplateGenerator class. PATH.parse( require.resolve('fluent-themes') ).dir, this.opts.theme ); - var exists = require('../utils/file-exists'); + var exists = require('path-exists').sync; if( !exists( tFolder ) ) { tFolder = PATH.resolve( this.opts.theme ); if( !exists( tFolder ) ) { diff --git a/src/utils/file-exists.js b/src/utils/file-exists.js deleted file mode 100644 index ef99ef9..0000000 --- a/src/utils/file-exists.js +++ /dev/null @@ -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'); - } -}; diff --git a/src/verbs/generate.js b/src/verbs/generate.js index 4fa10ff..88857c8 100644 --- a/src/verbs/generate.js +++ b/src/verbs/generate.js @@ -51,7 +51,7 @@ // Verify the specified theme name/path var relativeThemeFolder = '../../node_modules/fluent-themes/themes'; var tFolder = PATH.resolve( __dirname, relativeThemeFolder, _opts.theme); - var exists = require('../utils/file-exists'); + var exists = require('path-exists').sync; if (!exists( tFolder )) { tFolder = PATH.resolve( _opts.theme ); if (!exists( tFolder )) { diff --git a/src/verbs/validate.js b/src/verbs/validate.js index f5fb3be..52bd6da 100644 --- a/src/verbs/validate.js +++ b/src/verbs/validate.js @@ -15,7 +15,7 @@ var validator = require('is-my-json-valid'); var schemas = { - fresh: require('FRESCA'), + fresh: require('fresca'), jars: require('../core/resume.json') }; diff --git a/tests/resumes/jrs-0.0.0/empty.json b/tests/resumes/jrs-0.0.0/empty.json new file mode 100644 index 0000000..120d0c2 --- /dev/null +++ b/tests/resumes/jrs-0.0.0/empty.json @@ -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": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/tests/resumes/jrs-0.0.0/jane-incomplete.json b/tests/resumes/jrs-0.0.0/jane-incomplete.json new file mode 100644 index 0000000..3aa670d --- /dev/null +++ b/tests/resumes/jrs-0.0.0/jane-incomplete.json @@ -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" + } + ] +} diff --git a/tests/resumes/jrs/jane-q-fullstacker.json b/tests/resumes/jrs-0.0.0/jane-q-fullstacker.json similarity index 100% rename from tests/resumes/jrs/jane-q-fullstacker.json rename to tests/resumes/jrs-0.0.0/jane-q-fullstacker.json diff --git a/tests/resumes/jrs/richard-hendriks.json b/tests/resumes/jrs-0.0.0/richard-hendriks.json similarity index 100% rename from tests/resumes/jrs/richard-hendriks.json rename to tests/resumes/jrs-0.0.0/richard-hendriks.json diff --git a/tests/test-converter.js b/tests/test-converter.js index d66368e..c3cfb23 100644 --- a/tests/test-converter.js +++ b/tests/test-converter.js @@ -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 () { - 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' ); _sheet = new FRESHResume().open( fileA ); diff --git a/tests/test-fresh-sheet.js b/tests/test-fresh-sheet.js index 955c924..f35487d 100644 --- a/tests/test-fresh-sheet.js +++ b/tests/test-fresh-sheet.js @@ -55,7 +55,7 @@ describe('jane-doe.json (FRESH)', function () { it('should validate against the FRESH resume schema', function() { var result = _sheet.isValid(); - // var schemaJson = require('FRESCA'); + // var schemaJson = require('fresca'); // var validate = validator( schemaJson, { verbose: true } ); // var result = validate( JSON.parse( _sheet.imp.raw ) ); result || console.log("\n\nOops, resume didn't validate. " + diff --git a/tests/test-jrs-sheet.js b/tests/test-jrs-sheet.js index ee4b1b1..acd6f2e 100644 --- a/tests/test-jrs-sheet.js +++ b/tests/test-jrs-sheet.js @@ -9,55 +9,61 @@ var chai = require('chai') 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; - it('should open without throwing an exception', function () { + it('should open without throwing an exception', function () { + var that = this; function tryOpen() { _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(); }); it('should have one or more of each section', function() { - expect( - (_sheet.basics) && - (_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 ); + var newObj = _.pick( _sheet, opts.sections ); + expect( Object.keys(newObj).length ).to.equal( opts.sections.length ); }); - it('should have a work duration of 7 years', function() { - _sheet.basics.computed.numYears.should.equal( 7 ); + it('should have a work duration of ' + opts.duration + ' years', function() { + _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() { - _sheet.save( 'tests/sandbox/jane-q-fullstacker.json' ); + _sheet.save( 'tests/sandbox/' + opts.title + '.json' ); } trySave.should.not.Throw(); }); 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() ) }); - 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 schemaJson = require('../src/core/resume.json'); // var validate = validator( schemaJson, { verbose: true } ); // var result = validate( JSON.parse( _sheet.imp.raw ) ); result || console.log("\n\nOops, resume didn't validate. " + "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 });