1
0
mirror of https://github.com/JuanCanham/HackMyResume.git synced 2025-05-12 00:27:08 +01:00

Compare commits

...

43 Commits

Author SHA1 Message Date
786b3fd3b2 Merge pull request #25 from fluentdesk/v0.11.0
v0.11.0
2015-12-19 08:58:59 -05:00
f0a22be731 Skip underscore-prefixed folders during theme load. 2015-12-19 08:39:36 -05:00
ade60022fd Introduce new helpers. 2015-12-19 00:24:21 -05:00
7daba910ed Bump fluent-themes version. 2015-12-18 17:27:40 -05:00
a016d6d91a Update README. 2015-12-18 17:23:40 -05:00
fcaa97ed35 Change author info. 2015-12-18 15:35:00 -05:00
bb7373a229 Remove duplicate is-my-json-valid dependency. 2015-12-18 15:34:52 -05:00
759dcc30e7 Update tests. 2015-12-18 15:34:30 -05:00
0e47f02a33 Rename tests/jane-doe.json. 2015-12-18 15:34:00 -05:00
5fe90517e7 Auto-clean tests folder. 2015-12-18 15:33:18 -05:00
92128da381 Install URL-trimming helper. 2015-12-18 14:51:51 -05:00
1441fe3ae5 Class-ify Underscore/Handlebars engine. 2015-12-18 13:17:07 -05:00
b0bc71cd66 Introduce "either" helper for Handlebars themes. 2015-12-18 13:00:47 -05:00
e908e8bb34 Add missing Underscore require(). 2015-12-18 10:13:50 -05:00
d708a6c6d8 Refactor Handlebars helpers. 2015-12-18 10:10:30 -05:00
a630741098 Fix exception. 2015-12-18 10:10:19 -05:00
01d148e47c Bump version to 0.11.0. 2015-12-18 10:08:52 -05:00
dbd41ec439 Bump FRESCA version. 2015-12-17 14:28:24 -05:00
fc9cbab974 Choose template engine from active theme. 2015-12-17 11:04:29 -05:00
36f8010ebc Merge pull request #24 from fluentdesk/feat-notices
feat-notices
2015-12-17 10:27:58 -05:00
e80d8fb5c8 Bump version. 2015-12-17 10:29:22 -05:00
eabab26eef Update file headers. 2015-12-17 10:15:59 -05:00
18dbb23168 Merge pull request #23 from fluentdesk/v0.10.2
v0.10.2
2015-12-17 07:52:21 -05:00
9ad2a1e92e Add simple-html-tokenizer dependency. 2015-12-16 23:28:57 -05:00
5475b081b1 Support basic Markdown in MS Word docs. 2015-12-16 23:26:53 -05:00
ae9c295ce1 Better Handlebars support. 2015-12-16 20:13:27 -05:00
e0ef774692 Add missing semicolon. 2015-12-16 12:13:50 -05:00
0c1364593a Support post-save callback. 2015-12-16 11:26:30 -05:00
1603a4bc73 Prosecute FCVD updates. 2015-12-16 11:25:50 -05:00
8361cf9960 Remove extraneous comma.
Go away, extraneous comma.
2015-12-16 11:25:04 -05:00
8273e7d150 ... 2015-12-15 06:20:06 -05:00
5c49a8297f Fix: FCVD error. 2015-12-14 07:32:41 -05:00
ee1e4bf699 Scrub. 2015-12-12 11:13:47 -05:00
f5a8e36e50 Refactor theme tests. 2015-12-12 11:13:37 -05:00
b38a7c1da2 Improve conversions and tests. 2015-12-12 10:48:26 -05:00
fe2247329e Bump version to 0.10.2. 2015-12-12 04:43:29 -05:00
9d459370ce Update FRESH<-->JRS converter. 2015-12-12 04:42:56 -05:00
201d96fe22 Merge pull request #22 from fluentdesk/v0.10.1
v0.10.1
2015-12-11 22:29:58 -05:00
8747429bc6 Update NPM registry description. 2015-12-11 22:35:26 -05:00
95540efe29 Tweak FRESCA and theme dependency versions. 2015-12-11 04:02:05 -05:00
0474dc7dbe Bump version to 0.10.1. 2015-12-11 03:04:34 -05:00
e5af6c38e0 Merge remote-tracking branch 'refs/remotes/origin/master' into v0.10.1 2015-12-11 03:01:31 -05:00
00e6407347 Kludge theme loading issue for FCVD.
Resolve in v0.11.0.
2015-12-10 10:28:19 -05:00
39 changed files with 971 additions and 373 deletions

View File

@ -17,6 +17,8 @@ module.exports = function (grunt) {
all: { src: ['tests/*.js'] }
},
clean: ['tests/sandbox'],
yuidoc: {
compile: {
name: '<%= pkg.name %>',
@ -46,9 +48,10 @@ module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-simple-mocha');
grunt.loadNpmTasks('grunt-contrib-yuidoc');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.registerTask('test', 'Test the FluentCV library.',
function( config ) { grunt.task.run( ['simplemocha:all'] ); });
function( config ) { grunt.task.run( ['clean','simplemocha:all'] ); });
grunt.registerTask('document', 'Generate FluentCV library documentation.',
function( config ) { grunt.task.run( ['yuidoc'] ); });
grunt.registerTask('default', [ 'jshint', 'test', 'yuidoc' ]);

View File

@ -1,12 +1,13 @@
fluentCV
========
*Create polished technical résumés and CVs in multiple formats from your command
line or shell. See [FluentCV Desktop][7] for the desktop version. OS X ~ Windows
~ Linux.*
*Create polished résumés and CVs in multiple formats from your command line or
shell. Author in clean Markdown and JSON, export to Word, HTML, PDF, LaTeX,
plain text, and other arbitrary formats.*
![](assets/fluentcv_cli_ubuntu.png)
![](assets/resume-bouqet.png)
FluentCV is a dev-friendly Swiss Army knife for resumes and CVs. Use it to:
FluentCV is a dev-friendly, local-only Swiss Army knife for resumes and CVs. Use
it to:
1. **Generate** HTML, Markdown, LaTeX, MS Word, PDF, plain text, JSON, XML,
YAML, print, smoke signal, carrier pigeon, and other arbitrary-format resumes
@ -14,7 +15,10 @@ and CVs, from a single source of truth&mdash;without violating DRY.
2. **Convert** resumes between [FRESH][fresca] and [JSON Resume][6] formats.
3. **Validate** resumes against either format.
FluentCV supports both the [FRESH][fresca] and [JSON Resume][6] source formats.
FluentCV is built with Node.js and runs on recent versions of OS X, Linux, or
Windows.
![](assets/fluentcv_cli_ubuntu.png)
## Features

BIN
assets/resume-bouqet.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

View File

@ -1,7 +1,7 @@
{
"name": "fluentcv",
"version": "0.10.0",
"description": "Generate beautiful, targeted resumes from your command line, shell, or in Javascript with Node.js.",
"version": "0.11.0",
"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",
"url": "https://github.com/fluentdesk/fluentcv.git"
@ -20,7 +20,7 @@
"HTML",
"CLI"
],
"author": "James M. Devlin",
"author": "hacksalot <hacksalot@fluentdesk.com> (https://github.com/hacksalot)",
"license": "MIT",
"preferGlobal": "true",
"bugs": {
@ -32,9 +32,9 @@
},
"homepage": "https://github.com/fluentdesk/fluentcv",
"dependencies": {
"fresca": "^0.2.0",
"colors": "^1.1.2",
"fluent-themes": "0.6.0-beta",
"fluent-themes": "~0.7.0-beta",
"fresca": "~0.2.2",
"fs-extra": "^0.24.0",
"handlebars": "^4.0.5",
"html": "0.0.10",
@ -45,6 +45,7 @@
"mkdirp": "^0.5.1",
"moment": "^2.10.6",
"recursive-readdir-sync": "^1.0.6",
"simple-html-tokenizer": "^0.2.0",
"underscore": "^1.8.3",
"wkhtmltopdf": "^0.1.5",
"xml-escape": "^1.0.0",
@ -53,10 +54,11 @@
"devDependencies": {
"chai": "*",
"grunt": "*",
"grunt-contrib-clean": "^0.7.0",
"grunt-contrib-jshint": "^0.11.3",
"grunt-contrib-yuidoc": "^0.10.0",
"grunt-simple-mocha": "*",
"is-my-json-valid": "^2.12.2",
"jane-q-fullstacker": "fluentdesk/jane-q-fullstacker",
"mocha": "*",
"resample": "fluentdesk/resample"
}

View File

@ -1,6 +1,7 @@
/**
FRESH to JSON Resume conversion routiens.
@license MIT. Copyright (c) 2015 James M. Devlin / FluentDesk
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
@module convert.js
*/
(function(){
@ -14,6 +15,7 @@ FRESH to JSON Resume conversion routiens.
/**
Convert from JSON Resume format to FRESH.
@method toFresh
*/
toFRESH: function( src, foreign ) {
@ -47,87 +49,15 @@ FRESH to JSON Resume conversion routiens.
address: src.basics.location.address
},
employment: {
history: src.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') || undefined,
start: job.startDate,
end: job.endDate,
url: job.website,
keywords: "",
highlights: job.highlights
};
})
},
education: {
history: src.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,
area: edu.area,
studyType: edu.studyType
};
})
},
service: {
history: src.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
};
})
},
employment: employment( src.work, true ),
education: education( src.education, true),
service: service( src.volunteer, true),
skills: skillsToFRESH( src.skills ),
writing: src.publications.map(function(pub){
return {
title: pub.name,
flavor: undefined,
publisher: pub.publisher,
url: pub.website,
date: pub.releaseDate,
summary: pub.summary
};
}),
recognition: src.awards.map(function(awd){
return {
title: awd.title,
date: awd.date,
summary: awd.summary,
from: awd.awarder,
url: null
};
}),
social: src.basics.profiles.map(function(pro){
return {
label: pro.network,
network: pro.network,
url: pro.url,
user: pro.username
};
}),
writing: writing( src.publications, true),
recognition: recognition( src.awards, true, foreign ),
social: social( src.basics.profiles, true ),
interests: src.interests,
references: src.references,
testimonials: references( src.references, true ),
languages: src.languages,
disposition: src.disposition // <--> round-trip
};
@ -160,77 +90,17 @@ FRESH to JSON Resume conversion routiens.
countryCode: src.location.country,
region: src.location.region
},
profiles: src.social.map(function(soc){
return {
network: soc.network,
username: soc.user,
url: soc.url
};
})
profiles: social( src.social, false )
},
work: src.employment.history.map(function(emp){
return {
company: emp.employer,
website: emp.url,
position: emp.position,
startDate: emp.start,
endDate: emp.end,
summary: emp.summary,
highlights: emp.highlights
};
}),
education: src.education.history.map(function(edu){
return {
institution: edu.institution,
gpa: edu.grade,
courses: edu.curriculum,
startDate: edu.start,
endDate: edu.end,
area: edu.area,
studyType: edu.studyType
};
}),
skills: skillsToJRS( src.skills ),
volunteer: src.service.history.map(function(srv){
return {
flavor: foreign ? srv.flavor : undefined,
organization: srv.organization,
position: srv.position,
startDate: srv.start,
endDate: srv.end,
website: srv.url,
summary: srv.summary,
highlights: srv.highlights
};
}),
awards: src.recognition.map(function(awd){
return {
flavor: foreign ? awd.flavor : undefined,
url: foreign ? awd.url: undefined,
title: awd.title,
date: awd.date,
awarder: awd.from,
summary: awd.summary
};
}),
publications: src.writing.map(function(pub){
return {
name: pub.title,
publisher: pub.publisher,
releaseDate: pub.date,
website: pub.url,
summary: pub.summary
};
}),
work: employment( src.employment, false ),
education: education( src.education, false ),
skills: skillsToJRS( src.skills, false ),
volunteer: service( src.service, false ),
awards: recognition( src.recognition, false, foreign ),
publications: writing( src.writing, false ),
interests: src.interests,
references: src.references,
references: references( src.testimonials, false ),
samples: foreign ? src.samples : undefined,
disposition: foreign ? src.disposition : undefined,
languages: src.languages
@ -250,6 +120,207 @@ FRESH to JSON Resume conversion routiens.
return obj;
}
function employment( obj, direction ) {
if( !direction ) {
return obj && obj.history ?
obj.history.map(function(emp){
return {
company: emp.employer,
website: emp.url,
position: emp.position,
startDate: emp.start,
endDate: emp.end,
summary: emp.summary,
highlights: emp.highlights
};
}) : undefined;
}
else {
return {
history: obj && obj.length ?
obj.map( function( job ) {
return {
position: job.position,
employer: job.company,
summary: job.summary,
current: (!job.endDate || !job.endDate.trim() || job.endDate.trim().toLowerCase() === 'current') || undefined,
start: job.startDate,
end: job.endDate,
url: job.website,
keywords: "",
highlights: job.highlights
};
}) : undefined
};
}
}
function education( obj, direction ) {
if( direction ) {
return obj && obj.length ? {
history: obj.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,
area: edu.area,
studyType: edu.studyType
};
})
} : undefined;
}
else {
return obj && obj.history ?
obj.history.map(function(edu){
return {
institution: edu.institution,
gpa: edu.grade,
courses: edu.curriculum,
startDate: edu.start,
endDate: edu.end,
area: edu.area,
studyType: edu.studyType
};
}) : undefined;
}
}
function service( obj, direction, foreign ) {
if( direction ) {
return {
history: obj && obj.length ? obj.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
};
}) : undefined
};
}
else {
return obj && obj.history ?
obj.history.map(function(srv){
return {
flavor: foreign ? srv.flavor : undefined,
organization: srv.organization,
position: srv.position,
startDate: srv.start,
endDate: srv.end,
website: srv.url,
summary: srv.summary,
highlights: srv.highlights
};
}) : undefined;
}
}
function social( obj, direction ) {
if( direction ) {
return obj.map(function(pro){
return {
label: pro.network,
network: pro.network,
url: pro.url,
user: pro.username
};
});
}
else {
return obj.map( function( soc ) {
return {
network: soc.network,
username: soc.user,
url: soc.url
};
});
}
}
function recognition( obj, direction, foreign ) {
if( direction ) {
return obj && obj.length ? obj.map(
function(awd){
return {
flavor: foreign ? awd.flavor : undefined,
url: foreign ? awd.url: undefined,
title: awd.title,
date: awd.date,
from: awd.awarder,
summary: awd.summary
};
}) : undefined;
}
else {
return obj && obj.length ? obj.map(function(awd){
return {
flavor: foreign ? awd.flavor : undefined,
url: foreign ? awd.url: undefined,
title: awd.title,
date: awd.date,
awarder: awd.from,
summary: awd.summary
};
}) : undefined;
}
}
function references( obj, direction ) {
if( direction ) {
return obj && obj.length && obj.map(function(ref){
return {
name: ref.name,
flavor: 'professional',
quote: ref.reference,
private: false
};
});
}
else {
return obj && obj.length && obj.map(function(ref){
return {
name: ref.name,
reference: ref.quote
};
});
}
}
function writing( obj, direction ) {
if( direction ) {
return obj.map(function( pub ) {
return {
title: pub.name,
flavor: undefined,
publisher: pub.publisher,
url: pub.website,
date: pub.releaseDate,
summary: pub.summary
};
});
}
else {
return obj && obj.length ? obj.map(function(pub){
return {
name: pub.title,
publisher: pub.publisher && pub.publisher.name ? pub.publisher.name : pub.publisher,
releaseDate: pub.date,
website: pub.url,
summary: pub.summary
};
}) : undefined;
}
}
function skillsToFRESH( skills ) {
return {

View File

@ -47,6 +47,7 @@
"position": "",
"summary": "",
"start": "",
"end": "",
"keywords": [],
"highlights": []
}
@ -91,6 +92,7 @@
"sets": [
{
"name": "",
"level": "",
"skills": []
}
],

View File

@ -1,6 +1,7 @@
/**
The FluentCV date representation.
@license Copyright (c) 2015 by James M. Devlin. All rights reserved.
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
@module fluent-date.js
*/
var moment = require('moment');
@ -66,7 +67,10 @@ FluentDate/*.prototype*/.fmt = function( dt ) {
}
}
else {
if( dt.isValid && dt.isValid() )
if( !dt ) {
return moment();
}
else if( dt.isValid && dt.isValid() )
return dt;
throw 'Unknown date object encountered.';
}

View File

@ -1,6 +1,7 @@
/**
Definition of the FRESHResume class.
@license MIT. Copyright (c) 2015 James M. Devlin / FluentDesk
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
@module fresh-resume.js
*/
(function() {
@ -12,7 +13,8 @@ Definition of the FRESHResume class.
, PATH = require('path')
, moment = require('moment')
, MD = require('marked')
, CONVERTER = require('./convert');
, CONVERTER = require('./convert')
, JRSResume = require('./jrs-resume');
/**
A FRESH-style resume in JSON or YAML.
@ -46,13 +48,14 @@ Definition of the FRESHResume class.
Save the sheet to disk in a specific format, either FRESH or JSON Resume.
*/
FreshResume.prototype.saveAs = function( filename, format ) {
this.imp.fileName = filename || this.imp.fileName;
if( format !== 'JRS' ) {
this.imp.fileName = filename || this.imp.fileName;
FS.writeFileSync( this.imp.fileName, this.stringify(), 'utf8' );
}
else {
var newRep = CONVERTER.toJRS( this );
FS.writeFileSync( this.imp.fileName, FreshResume.stringify( newRep ), 'utf8' );
FS.writeFileSync( filename, JRSResume.stringify( newRep ), 'utf8' );
}
return this;
};
@ -211,6 +214,16 @@ Definition of the FRESHResume class.
delete this.social;
};
/**
Get a safe count of the number of things in a section.
*/
FreshResume.prototype.count = function( obj ) {
if( !obj ) return 0;
if( obj.history ) return obj.history.length;
if( obj.sets ) return obj.sets.length;
return obj.length || 0;
};
/**
Get the default (empty) sheet.
*/
@ -224,9 +237,18 @@ Definition of the FRESHResume class.
*/
FreshResume.prototype.add = function( moniker ) {
var defSheet = FreshResume.default();
var newObject = $.extend( true, {}, defSheet[ moniker ][0] );
var newObject = defSheet[moniker].history ?
$.extend( true, {}, defSheet[ moniker ].history[0] ) :
(moniker === 'skills' ?
$.extend( true, {}, defSheet.skills.sets[0] ) :
$.extend( true, {}, defSheet[ moniker ][0] ));
this[ moniker ] = this[ moniker ] || [];
this[ moniker ].push( newObject );
if( this[ moniker ].history )
this[ moniker ].history.push( newObject );
else if( moniker === 'skills' )
this.skills.sets.push( newObject );
else
this[ moniker ].push( newObject );
return newObject;
};
@ -300,14 +322,15 @@ Definition of the FRESHResume class.
*/
FreshResume.prototype.duration = function() {
if( this.employment.history && this.employment.history.length ) {
var careerStart = this.employment.history[ this.employment.history.length - 1].safe.start;
var firstJob = _.last( this.employment.history );
var careerStart = firstJob.start ? firstJob.safe.start : '';
if ((typeof careerStart === 'string' || careerStart instanceof String) &&
!careerStart.trim())
return 0;
var careerLast = _.max( this.employment.history, function( w ) {
return w.safe.end.unix();
}).safe.end;
return careerLast.diff( careerStart, 'years' );
return( w.safe && w.safe.end ) ? w.safe.end.unix() : moment().unix();
});
return careerLast.safe.end.diff( careerStart, 'years' );
}
return 0;
};
@ -366,7 +389,7 @@ Definition of the FRESHResume class.
replaceDatesInObject( obj[key] );
});
['start','end','date'].forEach( function(val) {
if( obj[val] && (!obj.safe || !obj.safe[val] )) {
if( (obj[val] !== undefined) && (!obj.safe || !obj.safe[val] )) {
obj.safe = obj.safe || { };
obj.safe[ val ] = _fmt( obj[val] );
if( obj[val] && (val === 'start') && !obj.end ) {

View File

@ -1,6 +1,7 @@
/**
Definition of the JRSResume class.
@license MIT. Copyright (c) 2015 James M. Devlin / FluentDesk
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
@module jrs-resume.js
*/
(function() {
@ -30,17 +31,22 @@ Definition of the JRSResume class.
consistent format. Then sort each section by startDate descending.
*/
JRSResume.prototype.open = function( file, title ) {
this.imp = { fileName: file };
this.imp.raw = FS.readFileSync( file, 'utf8' );
return this.parse( this.imp.raw, title );
//this.imp = { fileName: file }; <-- schema violation, tuck it into .basics instead
this.basics = {
imp: {
fileName: file,
raw: FS.readFileSync( file, 'utf8' )
}
};
return this.parse( this.basics.imp.raw, title );
};
/**
Save the sheet to disk (for environments that have disk access).
*/
JRSResume.prototype.save = function( filename ) {
this.imp.fileName = filename || this.imp.fileName;
FS.writeFileSync( this.imp.fileName, this.stringify(), 'utf8' );
this.basics.imp.fileName = filename || this.basics.imp.fileName;
FS.writeFileSync( this.basics.imp.fileName, this.stringify( this ), 'utf8' );
return this;
};
@ -48,7 +54,7 @@ Definition of the JRSResume class.
Convert this object to a JSON string, sanitizing meta-properties along the
way. Don't override .toString().
*/
JRSResume.prototype.stringify = function() {
JRSResume.stringify = function( obj ) {
function replacer( key,value ) { // Exclude these keys from stringification
return _.some(['imp', 'warnings', 'computed', 'filt', 'ctrl', 'index',
'safeStartDate', 'safeEndDate', 'safeDate', 'safeReleaseDate', 'result',
@ -56,7 +62,11 @@ Definition of the JRSResume class.
function( val ) { return key.trim() === val; }
) ? undefined : value;
}
return JSON.stringify( this, replacer, 2 );
return JSON.stringify( obj, replacer, 2 );
};
JRSResume.prototype.stringify = function() {
return JRSResume.stringify( this );
};
/**
@ -67,16 +77,17 @@ Definition of the JRSResume class.
JRSResume.prototype.parse = function( stringData, opts ) {
opts = opts || { };
var rep = JSON.parse( stringData );
extend( true, this, rep );
// Set up metadata
if( opts.imp === undefined || opts.imp ) {
this.imp = this.imp || { };
this.imp.title = (opts.title || this.imp.title) || this.basics.name;
this.basics.imp = this.basics.imp || { };
this.basics.imp.title = (opts.title || this.basics.imp.title) || this.basics.name;
}
// Parse dates, sort dates, and calculate computed values
(opts.date === undefined || opts.date) && _parseDates.call( this );
(opts.sort === undefined || opts.sort) && this.sort();
(opts.compute === undefined || opts.compute) && (this.computed = {
(opts.compute === undefined || opts.compute) && (this.basics.computed = {
numYears: this.duration(),
keywords: this.keywords()
});
@ -111,7 +122,7 @@ Definition of the JRSResume class.
JRSResume.prototype.clear = function( clearMeta ) {
clearMeta = ((clearMeta === undefined) && true) || clearMeta;
clearMeta && (delete this.imp);
delete this.computed; // Don't use Object.keys() here
delete this.basics.computed; // Don't use Object.keys() here
delete this.work;
delete this.volunteer;
delete this.education;
@ -169,8 +180,15 @@ Definition of the JRSResume class.
var schema = FS.readFileSync( PATH.join( __dirname, 'resume.json' ), 'utf8' );
var schemaObj = JSON.parse( schema );
var validator = require('is-my-json-valid');
var validate = validator( schemaObj );
return validate( this );
var validate = validator( schemaObj, { // Note [1]
formats: { date: /^\d{4}(?:-(?:0[0-9]{1}|1[0-2]{1})(?:-[0-9]{2})?)?$/ }
});
var ret = validate( this );
if( !ret ) {
this.basics.imp = this.basics.imp || { };
this.basics.imp.validationErrors = validate.errors;
}
return ret;
};
/**

View File

@ -1,6 +1,7 @@
/**
Abstract theme representation.
Definition of the Theme class.
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
@module theme.js
*/
(function() {
@ -99,9 +100,19 @@ Abstract theme representation.
var outFmt = '', isMajor = false;
var portion = pathInfo.dir.replace(tplFolder,'');
if( portion && portion.trim() ) {
var reg = /^(?:\/|\\)(html|latex|doc|pdf)(?:\/|\\)?/ig;
if( portion[1] === '_' ) return;
var reg = /^(?:\/|\\)(html|latex|doc|pdf|partials)(?:\/|\\)?/ig;
var res = reg.exec( portion );
res && (outFmt = res[1]);
if( res ) {
if( res[1] !== 'partials' ) {
outFmt = res[1];
}
else {
that.partials = that.partials || [];
that.partials.push( { name: pathInfo.name, path: absPath } );
return null;
}
}
}
// Otherwise, the output format is inferred from the filename, as in
@ -138,11 +149,11 @@ Abstract theme representation.
});
// Now, get all the CSS files...
(this.cssFiles = fmts.filter(function( fmt ){ return fmt.ext === 'css'; }))
(this.cssFiles = fmts.filter(function( fmt ){ return fmt && (fmt.ext === 'css'); }))
.forEach(function( cssf ) {
// For each CSS file, get its corresponding HTML file
var idx = _.findIndex(fmts, function( fmt ) {
return fmt.pre === cssf.pre && fmt.ext === 'html';
return fmt && fmt.pre === cssf.pre && fmt.ext === 'html';
});
cssf.action = null;
fmts[ idx ].css = cssf.data;
@ -151,7 +162,7 @@ Abstract theme representation.
// Remove CSS files from the formats array
fmts = fmts.filter( function( fmt) {
return fmt.ext !== 'css';
return fmt && (fmt.ext !== 'css');
});
return formatsHash;

View File

@ -1,17 +1,49 @@
/**
Handlebars template generate for FluentCV.
@license MIT. Copyright (c) 2015 James M. Devlin / FluentDesk.
Definition of the HandlebarsGenerator class.
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
@module handlebars-generator.js
*/
(function() {
var _ = require('underscore');
var HANDLEBARS = require('handlebars');
module.exports = function( json, jst, format, cssInfo, opts ) {
var template = HANDLEBARS.compile(jst);
return template( { r: json, filt: opts.filters, cssInfo: cssInfo, headFragment: opts.headFragment || '' } );
var _ = require('underscore')
, HANDLEBARS = require('handlebars')
, FS = require('fs')
, registerHelpers = require('./handlebars-helpers');
/**
Perform template-based resume generation using Handlebars.js.
@class HandlebarsGenerator
*/
var HandlebarsGenerator = module.exports = {
generate: function( json, jst, format, cssInfo, opts, theme ) {
// Pre-compile any partials present in the theme.
_.each( theme.partials, function( el ) {
var tplData = FS.readFileSync( el.path, 'utf8' );
var compiledTemplate = HANDLEBARS.compile( tplData );
HANDLEBARS.registerPartial( el.name, compiledTemplate );
});
// Register necessary helpers.
registerHelpers();
// Compile and run the Handlebars template.
var template = HANDLEBARS.compile(jst);
return template({
r: format === 'html' || format === 'pdf' ? json.markdownify() : json,
RAW: json,
filt: opts.filters,
cssInfo: cssInfo,
headFragment: opts.headFragment || ''
});
}
};

View File

@ -0,0 +1,124 @@
/**
Template helper definitions for Handlebars.
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
@module handlebars-helpers.js
*/
(function() {
var HANDLEBARS = require('handlebars')
, MD = require('marked')
, H2W = require('../utils/html-to-wpml')
, moment = require('moment')
, _ = require('underscore');
/**
Register useful Handlebars helpers.
@method registerHelpers
*/
module.exports = function() {
// Set up a date formatting helper so we can do:
// {{formatDate val 'YYYY-MM'}}
HANDLEBARS.registerHelper("formatDate", function(datetime, format) {
return moment ? moment( datetime ).format( format ) : datetime;
});
// Set up a Markdown-to-WordProcessingML helper so we can do:
// {{wmpl val [true|false]}}
HANDLEBARS.registerHelper("wpml", function( txt, inline ) {
if(!txt) return '';
inline = (inline && !inline.hash) || false;
txt = inline ?
MD(txt.trim()).replace(/^\s*<p>|<\/p>\s*$/gi, '') :
MD(txt.trim());
txt = H2W( txt.trim() );
return txt;
});
// Set up a last-word helper so we can do:
// {{lastWord val [true|false]}}
HANDLEBARS.registerHelper("link", function( text, url ) {
return url && url.trim() ?
('<a href="' + url + '">' + text + '</a>') : text;
});
// Set up a last-word helper so we can do:
// {{lastWord val [true|false]}}
HANDLEBARS.registerHelper("lastWord", function( txt ) {
return txt && txt.trim() ? _.last( txt.split(' ') ) : '';
});
// Set up a skill colorizing helper:
// {{skillColor val}}
HANDLEBARS.registerHelper("skillColor", function( lvl ) {
switch(lvl) {
case 'beginner': return '#5CB85C';
case 'intermediate': return '#F1C40F';
case 'advanced': return '#428BCA';
case 'master': return '#C00000';
}
});
// Set up a skill colorizing helper:
// {{skillColor val}}
HANDLEBARS.registerHelper("skillHeight", function( lvl ) {
switch(lvl) {
case 'beginner': return '30';
case 'intermediate': return '16';
case 'advanced': return '8';
case 'master': return '0';
}
});
// Set up a Markdown-to-WordProcessingML helper so we can do:
// {{initialWords val [true|false]}}
HANDLEBARS.registerHelper("initialWords", function( txt ) {
return txt && txt.trim() ? _.initial( txt.split(' ') ).join(' ') : '';
});
// Set up a URL-trimming helper to drop the protocol so we can do:
// {{trimURL url}}
HANDLEBARS.registerHelper("trimURL", function( url ) {
return url && url.trim() ? url.trim().replace(/^https?:\/\//i, '') : '';
});
// Set up a URL-trimming helper to drop the protocol so we can do:
// {{trimURL url}}
HANDLEBARS.registerHelper("toLower", function( txt ) {
return txt && txt.trim() ? txt.toLowerCase() : '';
});
// Set up a Markdown-to-WordProcessingML helper so we can do:
// {{either A B}}
HANDLEBARS.registerHelper("either", function( lhs, rhs, options ) {
if (lhs || rhs) return options.fn(this);
});
// Set up a generic conditional helper so we can do:
// {{compare val otherVal operator="<"}}
// http://doginthehat.com.au/2012/02/comparison-block-helper-for-handlebars-templates/
HANDLEBARS.registerHelper('compare', function(lvalue, rvalue, options) {
if (arguments.length < 3)
throw new Error("Handlerbars Helper 'compare' needs 2 parameters");
var operator = options.hash.operator || "==";
var operators = {
'==': function(l,r) { return l == r; },
'===': function(l,r) { return l === r; },
'!=': function(l,r) { return l != r; },
'<': function(l,r) { return l < r; },
'>': function(l,r) { return l > r; },
'<=': function(l,r) { return l <= r; },
'>=': function(l,r) { return l >= r; },
'typeof': function(l,r) { return typeof l == r; }
};
if (!operators[operator])
throw new Error("Handlerbars Helper 'compare' doesn't know the operator "+operator);
var result = operators[operator](lvalue,rvalue);
return result ? options.fn(this) : options.inverse(this);
});
};
}());

View File

@ -1,37 +1,52 @@
/**
Underscore template generate for FluentCV.
@license MIT. Copyright (c) 2015 James M. Devlin / FluentDesk.
Definition of the UnderscoreGenerator class.
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
@module underscore-generator.js
*/
(function() {
var _ = require('underscore');
module.exports = function( json, jst, format, cssInfo, opts ) {
// Tweak underscore's default template delimeters
var delims = opts.themeObj.delimeters || opts.template;
if( opts.themeObj.delimeters ) {
delims = _.mapObject( delims, function(val,key) {
return new RegExp( val, "ig");
/**
Perform template-based resume generation using Underscore.js.
@class UnderscoreGenerator
*/
var UnderscoreGenerator = module.exports = {
generate: function( json, jst, format, cssInfo, opts, theme ) {
// Tweak underscore's default template delimeters
var delims = (opts.themeObj && opts.themeObj.delimeters) || opts.template;
if( opts.themeObj && opts.themeObj.delimeters ) {
delims = _.mapObject( delims, function(val,key) {
return new RegExp( val, "ig");
});
}
_.templateSettings = delims;
// Strip {# comments #}
jst = jst.replace( delims.comment, '');
// Compile and run the template. TODO: avoid unnecessary recompiles.
var compiled = _.template(jst);
var ret = compiled({
r: format === 'html' || format === 'pdf' ? json.markdownify() : json,
filt: opts.filters,
XML: require('xml-escape'),
RAW: json,
cssInfo: cssInfo,
headFragment: opts.headFragment || ''
});
return ret;
}
_.templateSettings = delims;
// Strip {# comments #}
jst = jst.replace( delims.comment, '');
// Compile and run the template. TODO: avoid unnecessary recompiles.
var compiled = _.template(jst);
var ret = compiled({
r: format === 'html' || format === 'pdf' ? json.markdownify() : json,
filt: opts.filters,
XML: require('xml-escape'),
RAW: json,
cssInfo: cssInfo,
headFragment: opts.headFragment || ''
});
return ret;
};
}());

View File

@ -1,6 +1,6 @@
/**
Internal resume generation logic for FluentCV.
@license MIT. Copyright (c) 2015 James M. Devlin / FluentDesk
@license MIT. Copyright (c) 2015 James M. Devlin / FluentDesk.
@module fluentcmd.js
*/

View File

@ -1,6 +1,7 @@
/**
External API surface for FluentCV:CLI.
@license MIT. Copyright (c) 2015 James M. Devlin / FluentDesk
@license MIT. Copyright (c) 2015 James M. Devlin / FluentDesk.
@module fluentlib.js
*/
module.exports = {

View File

@ -1,6 +1,7 @@
/**
Base resume generator for FluentCV.
@license Copyright (c) 2015 | James M. Devlin
Definition of the BaseGenerator class.
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
@module base-generator.js
*/
(function() {

View File

@ -1,6 +1,7 @@
/**
HTML resume generator for FluentCV.
@license Copyright (c) 2015 James M. Devlin / FluentDesk
Definition of the HTMLGenerator class.
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
@module html-generator.js
*/
(function() {

View File

@ -1,6 +1,7 @@
/**
Definition of the HtmlPdfGenerator class.
@license Copyright (c) 2015 James M. Devlin / FluentDesk
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
@module html-pdf-generator.js
*/
(function() {

View File

@ -1,6 +1,7 @@
/**
Definition of the JsonGenerator class.
@license Copyright (c) 2015 | James M. Devlin
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
@module json-generator.js
*/
var BaseGenerator = require('./base-generator');

View File

@ -1,7 +1,7 @@
/**
A JSON-driven YAML resume generator for FluentLib.
Definition of the JsonYamlGenerator class.
@module json-yaml-generator.js
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
*/
(function() {

View File

@ -1,6 +1,7 @@
/**
LaTeX resume generator for FluentCV.
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk
Definition of the LaTeXGenerator class.
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
@module latex-generator.js
*/
var TemplateGenerator = require('./template-generator');

View File

@ -1,6 +1,7 @@
/**
Markdown resume generator for FluentCV.
@license Copyright (c) 2015 by James M. Devlin. All rights reserved.
Definition of the MarkdownGenerator class.
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
@module markdown-generator.js
*/
var TemplateGenerator = require('./template-generator');

View File

@ -1,6 +1,7 @@
/**
Template-based resume generator base for FluentCV.
@license MIT. Copyright (c) 2015 James M. Devlin / FluentDesk.
Definition of the TemplateGenerator class.
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
@module template-generator.js
*/
(function() {
@ -121,7 +122,8 @@ Template-based resume generator base for FluentCV.
Object.keys( curFmt.symLinks ).forEach( function(loc) {
var absLoc = PATH.join(outFolder, loc);
var absTarg = PATH.join(PATH.dirname(absLoc), curFmt.symLinks[loc]);
var type = PATH.parse( absLoc ).ext ? 'file' : 'junction'; // 'file', 'dir', or 'junction' (Windows only)
// 'file', 'dir', or 'junction' (Windows only)
var type = PATH.parse( absLoc ).ext ? 'file' : 'junction';
FS.symlinkSync( absTarg, absLoc, type);
});
}
@ -138,10 +140,10 @@ Template-based resume generator base for FluentCV.
@param cssInfo Needs to be refactored.
@param opts Options and passthrough data.
*/
single: function( json, jst, format, cssInfo, opts ) {
single: function( json, jst, format, cssInfo, opts, theme ) {
this.opts.freezeBreaks && ( jst = freeze(jst) );
var eng = require( '../eng/' + opts.themeObj.engine + '-generator' );
var result = eng( json, jst, format, cssInfo, opts );
var eng = require( '../eng/' + theme.engine + '-generator' );
var result = eng.generate( json, jst, format, cssInfo, opts, theme );
this.opts.freezeBreaks && ( result = unfreeze(result) );
return result;
}
@ -191,12 +193,13 @@ Template-based resume generator base for FluentCV.
*/
function transform( rez, f, tplInfo, theme, outFolder ) {
var cssInfo = { file: tplInfo.css ? tplInfo.cssPath : null, data: tplInfo.css || null };
var mk = this.single( rez, tplInfo.data, this.format, cssInfo, this.opts );
var mk = this.single( rez, tplInfo.data, this.format, cssInfo, this.opts, theme );
this.onBeforeSave && (mk = this.onBeforeSave( { mk: mk, theme: theme, outputFile: f } ));
var thisFilePath = PATH.join( outFolder, tplInfo.orgPath );
try {
MKDIRP.sync( PATH.dirname( tplInfo.major ? f : thisFilePath) );
FS.writeFileSync( tplInfo.major ? f : thisFilePath, mk, { encoding: 'utf8', flags: 'w' } );
this.onAfterSave && (mk = this.onAfterSave( { outputFile: (tplInfo.major ? f : thisFilePath), mk: mk } ));
}
catch(ex) {
console.log(ex);

View File

@ -1,6 +1,7 @@
/**
Plain text resume generator for FluentCV.
@license Copyright (c) 2015 by James M. Devlin. All rights reserved.
Definition of the TextGenerator class.
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
@module text-generator.js
*/
var TemplateGenerator = require('./template-generator');

View File

@ -1,13 +1,19 @@
/**
MS Word resume generator for FluentCV.
@license Copyright (c) 2015 by James M. Devlin. All rights reserved.
Definition of the WordGenerator class.
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
@module word-generator.js
*/
var TemplateGenerator = require('./template-generator');
var WordGenerator = module.exports = TemplateGenerator.extend({
(function() {
init: function(){
this._super( 'doc', 'xml' );
},
var TemplateGenerator = require('./template-generator');
var WordGenerator = module.exports = TemplateGenerator.extend({
});
init: function(){
this._super( 'doc', 'xml' );
}
});
}());

View File

@ -1,6 +1,7 @@
/**
XML resume generator for FluentCV.
@license Copyright (c) 2015 | James M. Devlin
Definition of the XMLGenerator class.
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
@module xml-generator.js
*/
var BaseGenerator = require('./base-generator');
@ -8,7 +9,7 @@ var BaseGenerator = require('./base-generator');
/**
The XmlGenerator generates an XML resume via the TemplateGenerator.
*/
var XmlGenerator = module.exports = BaseGenerator.extend({
var XMLGenerator = module.exports = BaseGenerator.extend({
init: function(){
this._super( 'xml' );

View File

@ -1,7 +1,7 @@
/**
A YAML resume generator for FluentLib.
Definition of the YAMLGenerator class.
@module yaml-generator.js
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
*/
@ -13,7 +13,7 @@ A YAML resume generator for FluentLib.
YamlGenerator generates a YAML-formatted resume via TemplateGenerator.
*/
var YamlGenerator = module.exports = TemplateGenerator.extend({
var YAMLGenerator = module.exports = TemplateGenerator.extend({
init: function(){
this._super( 'yml', 'yml' );

View File

@ -3,6 +3,7 @@
/**
Command-line interface (CLI) for FluentCV:CLI.
@license MIT. Copyright (c) 2015 James M. Devlin / FluentDesk.
@module index.js
*/
var ARGS = require( 'minimist' )

View File

@ -1,3 +1,8 @@
/**
Definition of John Resig's `Class` class.
@module class.js
*/
/* Simple JavaScript Inheritance
* By John Resig http://ejohn.org/
* MIT Licensed.

View File

@ -1,6 +1,7 @@
/**
Plain JavaScript replacement of jQuery .extend based on jQuery sources.
@license Copyright (c) 2015 by James M. Devlin. All rights reserved.
Definition of the `extend` method.
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
@module extend.js
*/
function _extend() {

View File

@ -1,6 +1,7 @@
/**
File-exists checker for Node.js.
@license Copyright (c) 2015 | James M. Devlin
Definition of the `fileExists` method.
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
@module file-exists.js
*/
var FS = require('fs');

64
src/utils/html-to-wpml.js Normal file
View File

@ -0,0 +1,64 @@
/**
Definition of the Markdown to WordProcessingML conversion routine.
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
@module html-to-wpml.js
*/
(function(){
var _ = require('underscore');
var HTML5Tokenizer = require('simple-html-tokenizer');
module.exports = function( html ) {
var final = '';
var is_bold = false, is_italic = false, is_link = false;
var depth = 0;
var tokens = HTML5Tokenizer.tokenize( html );
_.each( tokens, function( tok ) {
switch( tok.type ) {
case 'StartTag':
switch( tok.tagName ) {
case 'p':
final += '<w:p>';
break;
case 'strong':
is_bold = true;
break;
case 'em':
is_italic = true;
break;
case 'a':
is_link = true;
break;
}
break;
case 'EndTag':
switch( tok.tagName ) {
case 'p':
final += '</w:p>';
break;
case 'strong':
is_bold = false;
break;
case 'em':
is_italic = false;
break;
case 'a':
is_link = false;
break;
}
break;
case 'Chars':
var style = is_bold ? '<w:b/>' : '';
style += is_italic ? '<w:i/>': '';
final += '<w:r><w:rPr>' + style + '</w:rPr><w:t>' + tok.chars + '</w:t></w:r>';
break;
}
});
return final;
};
}());

View File

@ -1,6 +1,7 @@
/**
String utility functions.
@license Copyright (c) 2015 | James M. Devlin
Definitions of string utility functions.
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
@module string.js
*/
/**

View File

@ -0,0 +1,263 @@
{
"basics": {
"name": "Jane Q. Fullstacker",
"label": "Senior Developer",
"summary": "**Full-stack software developer with 6+ years industry experience** specializing in scalable cloud architectures for this, that, and the other. A native of southern CA, Jane enjoys hiking, mystery novels, and the company of Rufus, her two-year-old beagle.",
"website": "http://janef.me/blog",
"phone": "1-650-999-7777",
"email": "jdoe@onecoolstartup.io",
"picture": "jane_doe.png",
"location": {
"address": "Jane Fullstacker\n123 Somewhere Rd.\nMountain View, CA 94035",
"postalCode": "94035",
"city": "Mountain View",
"countryCode": "US",
"region": "CA"
},
"profiles": [
{
"network": "GitHub",
"username": "janef-was-here",
"url": "https://github.com/janef-was-here"
},
{
"network": "Twitter",
"username": "janef-was-here",
"url": "https://twitter.com/janef-was-here"
}
]
},
"work": [
{
"company": "One Cool Startup",
"website": "https://onecool.io/does-not-exist",
"position": "Head Code Ninja",
"startDate": "2013-09",
"summary": "Development team manager for OneCoolApp and OneCoolWebsite, a free social network tiddlywink generator and lifestyle portal with over 200,000 users.",
"highlights": [
"Managed a 5-person development team",
"Accomplishment 2",
"Etc."
]
},
{
"company": "Veridian Dynamics",
"website": "https://en.wikipedia.org/wiki/Better_Off_Ted#Plot",
"position": "Principal Developer",
"startDate": "2011-07",
"endDate": "2013-08",
"summary": "Developer on numerous projects culminating in technical lead role for the [Jabberwocky project](http://betteroffted.wikia.com/wiki/Jabberwocky) and promotion to principal developer.",
"highlights": [
"Managed a 5-person development team",
"Accomplishment 2",
"Etc."
]
},
{
"company": "Stark Industries",
"position": "IT Administrator",
"startDate": "2008-10",
"endDate": "2011-06",
"summary": "Junior programmer with heavy code responsibilities. Promoted to intermediate role after 6 months.",
"highlights": [
"Promoted to intermediate developer after 6 months",
"Accomplishment 2",
"Etc."
]
},
{
"company": "Dunder Mifflin",
"position": "Intern",
"startDate": "2008-06",
"endDate": "2008-09",
"summary": "Performed IT administration and deployments for Dunder Mifflin.",
"highlights": [
"Supervised roll-out of Dunder Mifflin Infinity website.",
"Performed mission-critical system backups and ",
"Etc."
]
}
],
"education": [
{
"institution": "Cornell University",
"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",
"keywords": [
"Node.js",
"Angular.js",
"jQuery",
"Bootstrap",
"React.js",
"Backbone.js"
]
},
{
"name": "Database",
"keywords": [
"MySQL",
"PostgreSQL",
"NoSQL",
"ORM",
"Hibernate"
]
},
{
"name": "Cloud",
"keywords": [
"AWS",
"EC2",
"RDS",
"S3",
"Azure",
"Dropbox"
]
},
{
"name": "Project",
"keywords": [
"Agile",
"TFS",
"Unified Process",
"MS Project"
]
}
],
"volunteer": [
{
"organization": "Technology for Tots",
"position": "Technical Consultant",
"startDate": "2003-11",
"endDate": "2005-06",
"website": "http://technology-for-tots.org",
"summary": "Summary of this volunteer stint.",
"highlights": [
"Accomplishment 1",
"Accomplishment 2",
"etc"
]
},
{
"organization": "US Army Reserves",
"position": "NCO",
"startDate": "1999-11",
"endDate": "2003-06",
"website": "http://www.usar.army.mil/",
"summary": "Summary of this military stint.",
"highlights": [
"Accomplishment 1",
"Accomplishment 2",
"etc"
]
}
],
"awards": [
{
"title": "Honorable Mention",
"date": "2012",
"awarder": "Google"
},
{
"title": "Summa cum laude",
"date": "2012",
"awarder": "Cornell University"
}
],
"publications": [
{
"name": "Building User Interfaces with Electron and Atom",
"publisher": "Code Project",
"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",
"releaseDate": "2008",
"website": "http://url.to.publication.com/blah",
"summary": "A primer on the programming language of GORFF, whose for loops are coterminous with all of time and space."
}
],
"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",
"years": 10
}
]
}

View File

@ -7,6 +7,7 @@ var chai = require('chai')
, FRESHResume = require('../src/core/fresh-resume')
, CONVERTER = require('../src/core/convert')
, FS = require('fs')
, MKDIRP = require('mkdirp')
, _ = require('underscore');
chai.config.includeStack = false;
@ -17,10 +18,11 @@ 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, 'jrs-exemplar/richard-hendriks.json' );
var fileB = path.join( __dirname, 'sandbox/richard-hendriks.json' );
var fileA = path.join( __dirname, 'resumes/jrs/richard-hendriks.json' );
var fileB = path.join( __dirname, 'sandbox/richard-hendriks.json' );
_sheet = new FRESHResume().open( fileA );
MKDIRP.sync( path.parse(fileB).dir );
_sheet.saveAs( fileB, 'JRS' );
var rawA = FS.readFileSync( fileA, 'utf8' );

View File

@ -16,7 +16,7 @@ describe('jane-doe.json (FRESH)', function () {
it('should open without throwing an exception', function () {
function tryOpen() {
_sheet = new FRESHResume().open(
'node_modules/FRESCA/exemplar/jane-doe.json' );
'node_modules/jane-q-fullstacker/resume/jane-resume.json' );
}
tryOpen.should.not.Throw();
});
@ -43,13 +43,13 @@ describe('jane-doe.json (FRESH)', function () {
it('should save without throwing an exception', function(){
function trySave() {
_sheet.save( 'tests/sandbox/jane-doe.json' );
_sheet.save( 'tests/sandbox/jane-q-fullstacker.json' );
}
trySave.should.not.Throw();
});
it('should not be modified after saving', function() {
var savedSheet = new FRESHResume().open('tests/sandbox/jane-doe.json');
var savedSheet = new FRESHResume().open('tests/sandbox/jane-q-fullstacker.json');
_sheet.stringify().should.equal( savedSheet.stringify() )
});
@ -65,9 +65,3 @@ describe('jane-doe.json (FRESH)', function () {
});
// describe('subtract', function () {
// it('should return -1 when passed the params (1, 2)', function () {
// expect(math.subtract(1, 2)).to.equal(-1);
// });
// });

View File

@ -9,13 +9,14 @@ var chai = require('chai')
chai.config.includeStack = false;
describe('fullstack.json (JRS)', function () {
describe('jane-doe.json (JRS)', function () {
var _sheet;
it('should open without throwing an exception', function () {
function tryOpen() {
_sheet = new JRSResume().open( 'node_modules/resample/fullstack/in/resume.json' );
_sheet = new JRSResume().open(
path.join( __dirname, 'resumes/jrs/jane-q-fullstacker.json' ) );
}
tryOpen.should.not.Throw();
});
@ -32,36 +33,31 @@ describe('fullstack.json (JRS)', function () {
).to.equal( true );
});
it('should have a work duration of 11 years', function() {
_sheet.computed.numYears.should.equal( 11 );
it('should have a work duration of 7 years', function() {
_sheet.basics.computed.numYears.should.equal( 7 );
});
it('should save without throwing an exception', function(){
function trySave() {
_sheet.save( 'tests/sandbox/fullstack.json' );
_sheet.save( 'tests/sandbox/jane-q-fullstacker.json' );
}
trySave.should.not.Throw();
});
it('should not be modified after saving', function() {
var savedSheet = new JRSResume().open( 'tests/sandbox/fullstack.json' );
var savedSheet = new JRSResume().open( 'tests/sandbox/jane-q-fullstacker.json' );
_sheet.stringify().should.equal( savedSheet.stringify() )
});
it('should validate against the JSON Resume schema', function() {
var schemaJson = require('../src/core/resume.json');
var validate = validator( schemaJson, { verbose: true } );
var result = validate( JSON.parse( _sheet.imp.raw ) );
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", validate.errors, "\n\n");
"Validation errors:\n\n", _sheet.basics.imp.validationErrors, "\n\n");
result.should.equal( true );
});
});
// describe('subtract', function () {
// it('should return -1 when passed the params (1, 2)', function () {
// expect(math.subtract(1, 2)).to.equal(-1);
// });
// });

View File

@ -26,85 +26,28 @@ describe('Testing themes', function () {
useful: 'green',
});
it('HELLO-WORLD theme should generate without throwing an exception', function () {
function tryOpen() {
var src = ['node_modules/FRESCA/exemplar/jane-doe.json'];
var dst = ['tests/sandbox/hello-world/resume.all'];
var opts = {
theme: 'hello-world',
format: 'FRESH',
prettify: true,
silent: false
};
FCMD.verbs.build( src, dst, opts );
}
tryOpen.should.not.Throw();
});
function genTheme( themeName ) {
it( themeName.toUpperCase() + ' theme should generate without throwing an exception', function () {
function tryOpen() {
var src = ['node_modules/jane-q-fullstacker/resume/jane-resume.json'];
var dst = ['tests/sandbox/' + themeName + '/resume.all'];
var opts = {
theme: themeName,
format: 'FRESH',
prettify: true,
silent: false
};
FCMD.verbs.build( src, dst, opts );
}
tryOpen.should.not.Throw();
});
}
it('COMPACT theme should generate without throwing an exception', function () {
function tryOpen() {
var src = ['node_modules/FRESCA/exemplar/jane-doe.json'];
var dst = ['tests/sandbox/compact/resume.all'];
var opts = {
theme: 'compact',
format: 'FRESH',
prettify: true,
silent: false
};
FCMD.verbs.build( src, dst, opts );
}
tryOpen.should.not.Throw();
});
it('MODERN theme should generate without throwing an exception', function () {
function tryOpen() {
var src = ['node_modules/FRESCA/exemplar/jane-doe.json'];
var dst = ['tests/sandbox/modern/resume.all'];
var opts = {
theme: 'modern',
format: 'FRESH',
prettify: true,
silent: false
};
FCMD.verbs.build( src, dst, opts );
}
tryOpen.should.not.Throw();
});
it('MINIMIST theme should generate without throwing an exception', function () {
function tryOpen() {
var src = ['node_modules/FRESCA/exemplar/jane-doe.json'];
var dst = ['tests/sandbox/minimist/resume.all'];
var opts = {
theme: 'minimist',
format: 'FRESH',
prettify: true,
silent: false
};
FCMD.verbs.build( src, dst, opts );
}
tryOpen.should.not.Throw();
});
it('AWESOME theme should generate without throwing an exception', function () {
function tryOpen() {
var src = ['node_modules/FRESCA/exemplar/jane-doe.json'];
var dst = ['tests/sandbox/awesome/resume.all'];
var opts = {
theme: 'awesome',
format: 'FRESH',
prettify: true,
silent: false
};
FCMD.verbs.build( src, dst, opts );
}
tryOpen.should.not.Throw();
});
genTheme('hello-world');
genTheme('compact');
genTheme('modern');
genTheme('minimist');
genTheme('awesome');
genTheme('positive');
});
// describe('subtract', function () {
// it('should return -1 when passed the params (1, 2)', function () {
// expect(math.subtract(1, 2)).to.equal(-1);
// });
// });