diff --git a/src/core/fresh-resume.js b/src/core/fresh-resume.js index 8af2149..4fd3192 100644 --- a/src/core/fresh-resume.js +++ b/src/core/fresh-resume.js @@ -36,9 +36,7 @@ Definition of the FRESHResume class. /** - Open and parse the specified FRESH resume sheet. Merge the JSON object model - onto this Sheet instance with extend() and convert sheet dates to a safe & - consistent format. Then sort each section by startDate descending. + Initialize the FreshResume from file. */ FreshResume.prototype.open = function( file, title ) { this.imp = { fileName: file }; @@ -48,6 +46,50 @@ Definition of the FRESHResume class. + /** + Initialize the the FreshResume from string. + */ + FreshResume.prototype.parse = function( stringData, opts ) { + return this.parseJSON( JSON.parse( stringData ), opts ); + }; + + + + /** + Initialize the FreshResume from JSON. + Open and parse the specified FRESH resume. Merge the JSON object model onto + this Sheet instance with extend() and convert sheet dates to a safe & + consistent format. Then sort each section by startDate descending. + */ + FreshResume.prototype.parseJSON = function( rep, opts ) { + // Convert JSON Resume to FRESH if necessary + if( rep.basics ) { + rep = CONVERTER.toFRESH( rep ); + rep.imp = rep.imp || { }; + rep.imp.orgFormat = 'JRS'; + } + + // Now apply the resume representation onto this object + extend( true, this, rep ); + + // Set up metadata + opts = opts || { }; + if( opts.imp === undefined || opts.imp ) { + this.imp = this.imp || { }; + this.imp.title = (opts.title || this.imp.title) || this.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 = { + numYears: this.duration(), + keywords: this.keywords() + }); + return this; + }; + + + /** Save the sheet to disk (for environments that have disk access). */ @@ -89,8 +131,8 @@ Definition of the FRESHResume class. /** - Convert the supplied object to a JSON string, sanitizing meta-properties along - the way. + Convert the supplied FreshResume to a JSON string, sanitizing meta-properties + along the way. */ FreshResume.stringify = function( obj ) { function replacer( key,value ) { // Exclude these keys from stringification @@ -104,6 +146,16 @@ Definition of the FRESHResume class. + /** + Convert this object to a JSON string, sanitizing meta-properties along the + way. + */ + FreshResume.prototype.stringify = function() { + return FreshResume.stringify( this ); + }; + + + /** Create a copy of this resume in which all string fields have been run through a transformation function (such as a Markdown filter or XML encoder). @@ -179,61 +231,14 @@ Definition of the FRESHResume class. Markdown. */ FreshResume.prototype.xmlify = function() { - function trx(key, val) { return XML(val); } - return this.transformStrings( [], trx ); }; - /** - Convert this object to a JSON string, sanitizing meta-properties along the - way. Don't override .toString(). - */ - FreshResume.prototype.stringify = function() { - return FreshResume.stringify( this ); - }; - - - - /** - Initialize the FreshResume from JSON data. - Open and parse the specified FRESH resume. Merge the JSON object model onto - this Sheet instance with extend() and convert sheet dates to a safe & - consistent format. Then sort each section by startDate descending. - */ - FreshResume.prototype.parseJSON = function( rep, opts ) { - // Convert JSON Resume to FRESH if necessary - if( rep.basics ) { - rep = CONVERTER.toFRESH( rep ); - rep.imp = rep.imp || { }; - rep.imp.orgFormat = 'JRS'; - } - - // Now apply the resume representation onto this object - extend( true, this, rep ); - - // Set up metadata - opts = opts || { }; - if( opts.imp === undefined || opts.imp ) { - this.imp = this.imp || { }; - this.imp.title = (opts.title || this.imp.title) || this.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 = { - numYears: this.duration(), - keywords: this.keywords() - }); - return this; - }; - - - /** Return the resume format. */ @@ -243,15 +248,6 @@ Definition of the FRESHResume class. - /** - Initialize the the FreshResume from string data. - */ - FreshResume.prototype.parse = function( stringData, opts ) { - return this.parseJSON( JSON.parse( stringData ), opts ); - }; - - - /** Return internal metadata. Create if it doesn't exist. */ @@ -285,7 +281,7 @@ Definition of the FRESHResume class. /** - Reset the sheet to an empty state. + Reset the sheet to an empty state. TODO: refactor/review */ FreshResume.prototype.clear = function( clearMeta ) { clearMeta = ((clearMeta === undefined) && true) || clearMeta; @@ -346,6 +342,7 @@ Definition of the FRESHResume class. }; + /** Determine if the sheet includes a specific social profile (eg, GitHub). */ @@ -403,7 +400,7 @@ Definition of the FRESHResume class. FreshResume.prototype.isValid = function( info ) { var schemaObj = require('fresca'); var validator = require('is-my-json-valid'); - var validate = validator( schemaObj, { // Note [1] + var validate = validator( schemaObj, { // See Note [1]. formats: { date: /^\d{4}(?:-(?:0[0-9]{1}|1[0-2]{1})(?:-[0-9]{2})?)?$/ } }); var ret = validate( this ); diff --git a/src/core/jrs-resume.js b/src/core/jrs-resume.js index edb5189..05bf70a 100644 --- a/src/core/jrs-resume.js +++ b/src/core/jrs-resume.js @@ -4,8 +4,12 @@ Definition of the JRSResume class. @module jrs-resume.js */ + + (function() { + + var FS = require('fs') , extend = require('../utils/extend') , validator = require('is-my-json-valid') @@ -15,25 +19,24 @@ Definition of the JRSResume class. , CONVERTER = require('./convert') , moment = require('moment'); + + /** - The JRSResume class represent a specific JSON character sheet. When Sheet.open - is called, we merge the loaded JSON sheet properties onto the Sheet instance - via extend(), so a full-grown sheet object will have all of the methods here, - plus a complement of JSON properties from the backing JSON file. That allows - us to treat Sheet objects interchangeably with the loaded JSON model. + A JRS resume or CV. JRS resumes are backed by JSON, and each JRSResume object + is an instantiation of that JSON decorated with utility methods. @class JRSResume */ function JRSResume() { } + + /** - Open and parse the specified JSON resume sheet. Merge the JSON object model - onto this Sheet instance with extend() and convert sheet dates to a safe & - consistent format. Then sort each section by startDate descending. + Initialize the JSResume from file. */ JRSResume.prototype.open = function( file, title ) { - //this.imp = { fileName: file }; <-- schema violation, tuck it into .basics instead + //this.imp = { fileName: file }; <-- schema violation, tuck it into .basics this.basics = { imp: { fileName: file, @@ -43,15 +46,58 @@ Definition of the JRSResume class. return this.parse( this.basics.imp.raw, title ); }; + + + /** + Initialize the the JSResume from string. + */ + JRSResume.prototype.parse = function( stringData, opts ) { + opts = opts || { }; + var rep = JSON.parse( stringData ); + return this.parseJSON( rep, opts ); + }; + + + + /** + Initialize the JRSResume from JSON. + Open and parse the specified JRS resume. Merge the JSON object model onto this + Sheet instance with extend() and convert sheet dates to a safe & consistent + format. Then sort each section by startDate descending. + */ + JRSResume.prototype.parseJSON = function( rep, opts ) { + opts = opts || { }; + extend( true, this, rep ); + // Set up metadata + if( opts.imp === undefined || opts.imp ) { + this.basics.imp = this.basics.imp || { }; + this.basics.imp.title = + (opts.title || this.basics.imp.title) || this.basics.name; + this.basics.imp.orgFormat = 'JRS'; + } + // 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.basics.computed = { + numYears: this.duration(), + keywords: this.keywords() + }); + return this; + }; + + + /** Save the sheet to disk (for environments that have disk access). */ JRSResume.prototype.save = function( filename ) { this.basics.imp.fileName = filename || this.basics.imp.fileName; - FS.writeFileSync( this.basics.imp.fileName, this.stringify( this ), 'utf8' ); + FS.writeFileSync(this.basics.imp.fileName, this.stringify( this ), 'utf8'); return this; }; + + /** Save the sheet to disk in a specific format, either FRESH or JRS. */ @@ -70,6 +116,8 @@ Definition of the JRSResume class. }; + + /** Return the resume format. */ @@ -77,6 +125,8 @@ Definition of the JRSResume class. return 'JRS'; }; + + /** Convert this object to a JSON string, sanitizing meta-properties along the way. Don't override .toString(). @@ -85,50 +135,20 @@ Definition of the JRSResume class. function replacer( key,value ) { // Exclude these keys from stringification return _.some(['imp', 'warnings', 'computed', 'filt', 'ctrl', 'index', 'safeStartDate', 'safeEndDate', 'safeDate', 'safeReleaseDate', 'result', - 'isModified', 'htmlPreview', 'display_progress_bar'], + 'isModified', 'htmlPreview', 'display_progress_bar'], function( val ) { return key.trim() === val; } ) ? undefined : value; } return JSON.stringify( obj, replacer, 2 ); }; + + JRSResume.prototype.stringify = function() { return JRSResume.stringify( this ); }; - /** - Initialize the JRS Resume from string data. - Open and parse the specified JSON resume sheet. Merge the JSON object model - onto this Sheet instance with extend() and convert sheet dates to a safe & - consistent format. Then sort each section by startDate descending. - */ - JRSResume.prototype.parse = function( stringData, opts ) { - opts = opts || { }; - var rep = JSON.parse( stringData ); - return this.parseJSON( rep, opts ); - }; - /** - Initialize the JRSRume from JSON data. - */ - JRSResume.prototype.parseJSON = function( rep, opts ) { - opts = opts || { }; - extend( true, this, rep ); - // Set up metadata - if( opts.imp === undefined || opts.imp ) { - this.basics.imp = this.basics.imp || { }; - this.basics.imp.title = (opts.title || this.basics.imp.title) || this.basics.name; - this.basics.imp.orgFormat = 'JRS'; - } - // 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.basics.computed = { - numYears: this.duration(), - keywords: this.keywords() - }); - return this; - }; /** Return a unique list of all keywords across all skills. @@ -143,6 +163,8 @@ Definition of the JRSResume class. return flatSkills; }; + + /** Return internal metadata. Create if it doesn't exist. JSON Resume v0.0.0 doesn't allow additional properties at the root level, @@ -153,6 +175,8 @@ Definition of the JRSResume class. return this.basics; }; + + /** Reset the sheet to an empty state. */ @@ -170,13 +194,19 @@ Definition of the JRSResume class. delete this.basics.profiles; }; + + /** Get the default (empty) sheet. */ JRSResume.default = function() { - return new JRSResume().open( PATH.join( __dirname, 'empty-jrs.json'), 'Empty' ); + return new JRSResume().open( + PATH.join( __dirname, 'empty-jrs.json'), 'Empty' + ); }; + + /** Add work experience to the sheet. */ @@ -188,6 +218,8 @@ Definition of the JRSResume class. return newObject; }; + + /** Determine if the sheet includes a specific social profile (eg, GitHub). */ @@ -198,6 +230,8 @@ Definition of the JRSResume class. }); }; + + /** Determine if the sheet includes a specific skill. */ @@ -210,11 +244,13 @@ Definition of the JRSResume class. }); }; + + /** Validate the sheet against the JSON Resume schema. */ JRSResume.prototype.isValid = function( ) { // TODO: ↓ fix this path ↓ - var schema = FS.readFileSync( PATH.join( __dirname, 'resume.json' ), 'utf8' ); + 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, { // Note [1] @@ -228,6 +264,8 @@ Definition of the JRSResume class. return ret; }; + + /** Calculate the total duration of the sheet. Assumes this.work has been sorted by start date descending, perhaps via a call to Sheet.sort(). @@ -250,6 +288,8 @@ Definition of the JRSResume class. return 0; }; + + /** Sort dated things on the sheet by start date descending. Assumes that dates on the sheet have been processed with _parseDates(). @@ -276,12 +316,16 @@ Definition of the JRSResume class. }; + + JRSResume.prototype.dupe = function() { var rnew = new JRSResume(); rnew.parse( this.stringify(), { } ); return rnew; }; + + /** Create a copy of this resume in which all fields have been interpreted as Markdown. @@ -319,7 +363,9 @@ Definition of the JRSResume class. Object.keys( obj ).forEach(function(key) { var sub = obj[key]; if( typeof sub === 'string' || sub instanceof String ) { - if( _.contains(['skills','url','website','startDate','endDate','releaseDate','date','phone','email','address','postalCode','city','country','region'], key) ) + if( _.contains(['skills','url','website','startDate','endDate', + 'releaseDate','date','phone','email','address','postalCode', + 'city','country','region'], key) ) return; if( key === 'summary' ) obj[key] = HD( obj[key] ); @@ -340,6 +386,8 @@ Definition of the JRSResume class. return ret; }; + + /** Convert human-friendly dates into formal Moment.js dates for all collections. We don't want to lose the raw textual date as entered by the user, so we store @@ -371,9 +419,13 @@ Definition of the JRSResume class. }); } + + /** Export the JRSResume function/ctor. */ module.exports = JRSResume; + + }());