mirror of
https://github.com/JuanCanham/HackMyResume.git
synced 2025-05-15 10:07:07 +01:00
Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
bd278268f6 | |||
abe31e30e0 | |||
314d8d8763 | |||
ed0792e8f8 | |||
90765bf90b | |||
f1ba7765ee | |||
27c7a0264a | |||
8e806dc04f | |||
8ec6b5ed6a | |||
4ef4ec5d42 | |||
2f523b845b | |||
1c416f39d3 | |||
1de0eff7b3 | |||
f8a39b0908 |
58
BUILDING.md
Normal file
58
BUILDING.md
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
Building
|
||||||
|
========
|
||||||
|
|
||||||
|
*See [CONTRIBUTING.md][contrib] for more information on contributing to the
|
||||||
|
HackMyResume or FluentCV projects.*
|
||||||
|
|
||||||
|
HackMyResume is a standard Node.js command line app implemented in a mix of
|
||||||
|
CoffeeScript and JavaScript. Setting up a build environment is easy:
|
||||||
|
|
||||||
|
|
||||||
|
## Prerequisites ##
|
||||||
|
|
||||||
|
1. OS: Linux, OS X, or Windows
|
||||||
|
|
||||||
|
2. Install [Node.js][node] and [Grunt][grunt].
|
||||||
|
|
||||||
|
|
||||||
|
## Set up a build environment ###
|
||||||
|
|
||||||
|
1. Fork [hacksalot/HackMyResume][hmr] to your GitHub account.
|
||||||
|
|
||||||
|
2. Clone your fork locally.
|
||||||
|
|
||||||
|
3. From within the top-level HackMyResume folder, run `npm install` to install
|
||||||
|
project dependencies.
|
||||||
|
|
||||||
|
4. Create a new branch, based on the latest HackMyResume `dev` branch, to
|
||||||
|
contain your work.
|
||||||
|
|
||||||
|
5. Run `npm link` in the HackMyResume folder so that the `hackmyresume` command
|
||||||
|
will reference your local installation (you may need to
|
||||||
|
`npm uninstall -g hackmyresume` first).
|
||||||
|
|
||||||
|
## Making changes
|
||||||
|
|
||||||
|
1. HackMyResume sources live in the [`/src`][src] folder. Always make your edits
|
||||||
|
there, never in the generated `/dist` folder.
|
||||||
|
|
||||||
|
2. After making your changes, run `grunt build` to package the HackMyResume
|
||||||
|
sources to the `/dist` folder. This will transform CoffeeScript files to
|
||||||
|
JavaScript and perform other build steps as necessary. In the future, a watch
|
||||||
|
task or guardfile will be added to automate this step.
|
||||||
|
|
||||||
|
3. Do local spot testing with `hackmyresume` as normal.
|
||||||
|
|
||||||
|
4. When you're ready to submit your changes, run `grunt test` to run the HMR
|
||||||
|
test suite. Fix any errors that occur.
|
||||||
|
|
||||||
|
5. Commit and push your changes.
|
||||||
|
|
||||||
|
6. Submit a pull request targeting the HackMyResume `dev` branch.
|
||||||
|
|
||||||
|
|
||||||
|
[node]: https://nodejs.org/en/
|
||||||
|
[grunt]: http://gruntjs.com/
|
||||||
|
[hmr]: https://github.com/hacksalot/HackMyResume
|
||||||
|
[src]: https://github.com/hacksalot/HackMyResume/tree/master/src
|
||||||
|
[contrib]: https://github.com/hacksalot/HackMyResume/blob/master/CONTRIBUTING.md
|
@ -4,17 +4,11 @@ Contributing
|
|||||||
*Note: HackMyResume is also available as [FluentCV][fcv]. Contributors are
|
*Note: HackMyResume is also available as [FluentCV][fcv]. Contributors are
|
||||||
credited in both.*
|
credited in both.*
|
||||||
|
|
||||||
HackMyResume needs your help! Our contribution workflow is based on [GitHub
|
|
||||||
Flow][flow] and we respond to all pull requests and issues, usually within 24
|
|
||||||
hours. HackMyResume has no corporate affiliation and no commercial basis, which
|
|
||||||
allows the project to maintain a strict user-first policy, rapid development
|
|
||||||
velocity, and a liberal stance on contributions and exotic functionality in
|
|
||||||
keeping with the spirit (and name) of the tool.
|
|
||||||
|
|
||||||
In short, your code is welcome here.
|
|
||||||
|
|
||||||
## How To Contribute
|
## How To Contribute
|
||||||
|
|
||||||
|
*See [BUILDING.md][building] for instructions on setting up a HackMyResume
|
||||||
|
development environment.*
|
||||||
|
|
||||||
1. Optional: [**open an issue**][iss] identifying the feature or bug you'd like
|
1. Optional: [**open an issue**][iss] identifying the feature or bug you'd like
|
||||||
to implement or fix. This step isn't required — you can start hacking away on
|
to implement or fix. This step isn't required — you can start hacking away on
|
||||||
HackMyResume without clearing it with us — but helps avoid duplication of work
|
HackMyResume without clearing it with us — but helps avoid duplication of work
|
||||||
@ -25,7 +19,7 @@ similar; call it whatever you like) to perform your work in.
|
|||||||
4. **Install dependencies** by running `npm install` in the top-level
|
4. **Install dependencies** by running `npm install` in the top-level
|
||||||
HackMyResume folder.
|
HackMyResume folder.
|
||||||
5. Make your **commits** as usual.
|
5. Make your **commits** as usual.
|
||||||
6. **Verify** your changes locally with `npm test`.
|
6. **Verify** your changes locally with `grunt test`.
|
||||||
7. **Push** your commits.
|
7. **Push** your commits.
|
||||||
7. **Submit a pull request** from your feature branch to the HackMyResume `dev`
|
7. **Submit a pull request** from your feature branch to the HackMyResume `dev`
|
||||||
branch.
|
branch.
|
||||||
@ -48,7 +42,7 @@ You can reach hacksalot directly at:
|
|||||||
hacksalot@indevious.com
|
hacksalot@indevious.com
|
||||||
```
|
```
|
||||||
|
|
||||||
Thanks! See you out there in the trenches.
|
Thanks for your interest in the HackMyResume project.
|
||||||
|
|
||||||
[fcv]: https://github.com/fluentdesk/fluentcv
|
[fcv]: https://github.com/fluentdesk/fluentcv
|
||||||
[flow]: https://guides.github.com/introduction/flow/
|
[flow]: https://guides.github.com/introduction/flow/
|
||||||
@ -56,3 +50,4 @@ Thanks! See you out there in the trenches.
|
|||||||
[ha]: https://github.com/hacksalot
|
[ha]: https://github.com/hacksalot
|
||||||
[th]: https://github.com/tomheon
|
[th]: https://github.com/tomheon
|
||||||
[awesome]: https://github.com/hacksalot/HackMyResume/graphs/contributors
|
[awesome]: https://github.com/hacksalot/HackMyResume/graphs/contributors
|
||||||
|
[building]: https://github.com/hacksalot/HackMyResume/blob/master/BUILDING.md
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
The MIT License
|
The MIT License
|
||||||
===============
|
===============
|
||||||
|
|
||||||
Copyright (c) 2016 hacksalot (https://github.com/hacksalot)
|
Copyright (c) 2015-2016 hacksalot (https://github.com/hacksalot)
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
71
dist/core/abstract-resume.js
vendored
Normal file
71
dist/core/abstract-resume.js
vendored
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
Definition of the AbstractResume class.
|
||||||
|
@license MIT. See LICENSE.md for details.
|
||||||
|
@module core/abstract-resume
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
var AbstractResume, FluentDate, _, __;
|
||||||
|
|
||||||
|
_ = require('underscore');
|
||||||
|
|
||||||
|
__ = require('lodash');
|
||||||
|
|
||||||
|
FluentDate = require('./fluent-date');
|
||||||
|
|
||||||
|
AbstractResume = (function() {
|
||||||
|
function AbstractResume() {}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Compute the total duration of the work history.
|
||||||
|
@returns The total duration of the sheet's work history, that is, the number
|
||||||
|
of years between the start date of the earliest job on the resume and the
|
||||||
|
*latest end date of all jobs in the work history*. This last condition is for
|
||||||
|
sheets that have overlapping jobs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
AbstractResume.prototype.duration = function(collKey, startKey, endKey, unit) {
|
||||||
|
var firstDate, hist, lastDate, new_e;
|
||||||
|
unit = unit || 'years';
|
||||||
|
hist = __.get(this, collKey);
|
||||||
|
if (!hist || !hist.length) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
new_e = hist.map(function(job) {
|
||||||
|
var obj;
|
||||||
|
obj = _.pick(job, [startKey, endKey]);
|
||||||
|
if (!_.has(obj, endKey)) {
|
||||||
|
obj[endKey] = 'current';
|
||||||
|
}
|
||||||
|
if (obj && (obj[startKey] || obj[endKey])) {
|
||||||
|
obj = _.pairs(obj);
|
||||||
|
obj[0][1] = FluentDate.fmt(obj[0][1]);
|
||||||
|
if (obj.length > 1) {
|
||||||
|
obj[1][1] = FluentDate.fmt(obj[1][1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
});
|
||||||
|
new_e = _.filter(_.flatten(new_e, true), function(v) {
|
||||||
|
return v && v.length && v[0] && v[0].length;
|
||||||
|
});
|
||||||
|
if (!new_e || !new_e.length) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
new_e = _.sortBy(new_e, function(elem) {
|
||||||
|
return elem[1].unix();
|
||||||
|
});
|
||||||
|
firstDate = _.first(new_e)[1];
|
||||||
|
lastDate = _.last(new_e)[1];
|
||||||
|
return lastDate.diff(firstDate, unit);
|
||||||
|
};
|
||||||
|
|
||||||
|
return AbstractResume;
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
module.exports = AbstractResume;
|
||||||
|
|
||||||
|
}).call(this);
|
36
dist/core/fluent-date.js
vendored
36
dist/core/fluent-date.js
vendored
@ -10,6 +10,8 @@ The HackMyResume date representation.
|
|||||||
|
|
||||||
moment = require('moment');
|
moment = require('moment');
|
||||||
|
|
||||||
|
require('../utils/string');
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Create a FluentDate from a string or Moment date object. There are a few date
|
Create a FluentDate from a string or Moment date object. There are a few date
|
||||||
@ -33,6 +35,10 @@ The HackMyResume date representation.
|
|||||||
this.rep = this.fmt(dt);
|
this.rep = this.fmt(dt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FluentDate.isCurrent = function(dt) {
|
||||||
|
return !dt || (String.is(dt) && /^(present|now|current)$/.test(dt));
|
||||||
|
};
|
||||||
|
|
||||||
return FluentDate;
|
return FluentDate;
|
||||||
|
|
||||||
})();
|
})();
|
||||||
@ -54,7 +60,7 @@ The HackMyResume date representation.
|
|||||||
module.exports = FluentDate;
|
module.exports = FluentDate;
|
||||||
|
|
||||||
FluentDate.fmt = function(dt, throws) {
|
FluentDate.fmt = function(dt, throws) {
|
||||||
var defTime, month, mt, parts, ref, temp;
|
var month, mt, parts, ref, temp;
|
||||||
throws = (throws === void 0 || throws === null) || throws;
|
throws = (throws === void 0 || throws === null) || throws;
|
||||||
if (typeof dt === 'string' || dt instanceof String) {
|
if (typeof dt === 'string' || dt instanceof String) {
|
||||||
dt = dt.toLowerCase().trim();
|
dt = dt.toLowerCase().trim();
|
||||||
@ -72,33 +78,7 @@ The HackMyResume date representation.
|
|||||||
} else if (/^\s*\d{4}\s*$/.test(dt)) {
|
} else if (/^\s*\d{4}\s*$/.test(dt)) {
|
||||||
return moment(dt, 'YYYY');
|
return moment(dt, 'YYYY');
|
||||||
} else if (/^\s*$/.test(dt)) {
|
} else if (/^\s*$/.test(dt)) {
|
||||||
defTime = {
|
return moment();
|
||||||
isNull: true,
|
|
||||||
isBefore: function(other) {
|
|
||||||
if (other && !other.isNull) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
isAfter: function(other) {
|
|
||||||
if (other && !other.isNull) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
unix: function() {
|
|
||||||
return 0;
|
|
||||||
},
|
|
||||||
format: function() {
|
|
||||||
return '';
|
|
||||||
},
|
|
||||||
diff: function() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return defTime;
|
|
||||||
} else {
|
} else {
|
||||||
mt = moment(dt);
|
mt = moment(dt);
|
||||||
if (mt.isValid()) {
|
if (mt.isValid()) {
|
||||||
|
64
dist/core/fresh-resume.js
vendored
64
dist/core/fresh-resume.js
vendored
@ -6,7 +6,9 @@ Definition of the FRESHResume class.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
var CONVERTER, FS, FreshResume, JRSResume, MD, PATH, XML, _, __, _parseDates, extend, moment, validator;
|
var AbstractResume, CONVERTER, FS, FluentDate, FreshResume, JRSResume, MD, PATH, XML, _, __, _parseDates, extend, moment, validator,
|
||||||
|
extend1 = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
|
||||||
|
hasProp = {}.hasOwnProperty;
|
||||||
|
|
||||||
FS = require('fs');
|
FS = require('fs');
|
||||||
|
|
||||||
@ -30,6 +32,10 @@ Definition of the FRESHResume class.
|
|||||||
|
|
||||||
JRSResume = require('./jrs-resume');
|
JRSResume = require('./jrs-resume');
|
||||||
|
|
||||||
|
FluentDate = require('./fluent-date');
|
||||||
|
|
||||||
|
AbstractResume = require('./abstract-resume');
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A FRESH resume or CV. FRESH resumes are backed by JSON, and each FreshResume
|
A FRESH resume or CV. FRESH resumes are backed by JSON, and each FreshResume
|
||||||
@ -37,8 +43,12 @@ Definition of the FRESHResume class.
|
|||||||
@constructor
|
@constructor
|
||||||
*/
|
*/
|
||||||
|
|
||||||
FreshResume = (function() {
|
FreshResume = (function(superClass) {
|
||||||
function FreshResume() {}
|
extend1(FreshResume, superClass);
|
||||||
|
|
||||||
|
function FreshResume() {
|
||||||
|
return FreshResume.__super__.constructor.apply(this, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Initialize the FreshResume from file. */
|
/** Initialize the FreshResume from file. */
|
||||||
@ -376,36 +386,8 @@ Definition of the FRESHResume class.
|
|||||||
return ret;
|
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().
|
|
||||||
@returns The total duration of the sheet's work history, that is, the number
|
|
||||||
of years between the start date of the earliest job on the resume and the
|
|
||||||
*latest end date of all jobs in the work history*. This last condition is for
|
|
||||||
sheets that have overlapping jobs.
|
|
||||||
*/
|
|
||||||
|
|
||||||
FreshResume.prototype.duration = function(unit) {
|
FreshResume.prototype.duration = function(unit) {
|
||||||
var careerLast, careerStart, empHist, firstJob;
|
return FreshResume.__super__.duration.call(this, 'employment.history', 'start', 'end', unit);
|
||||||
unit = unit || 'years';
|
|
||||||
empHist = __.get(this, 'employment.history');
|
|
||||||
if (empHist && empHist.length) {
|
|
||||||
firstJob = _.last(empHist);
|
|
||||||
careerStart = firstJob.start ? firstJob.safe.start : '';
|
|
||||||
if ((typeof careerStart === 'string' || careerStart instanceof String) && !careerStart.trim()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
careerLast = _.max(empHist, function(w) {
|
|
||||||
if (w.safe && w.safe.end) {
|
|
||||||
return w.safe.end.unix();
|
|
||||||
} else {
|
|
||||||
return moment().unix();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return careerLast.safe.end.diff(careerStart, unit);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -420,7 +402,11 @@ Definition of the FRESHResume class.
|
|||||||
if (a.safe.start.isBefore(b.safe.start)) {
|
if (a.safe.start.isBefore(b.safe.start)) {
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
return (a.safe.start.isAfter(b.safe.start) && -1) || 0;
|
if (a.safe.start.isAfter(b.safe.start)) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
sortSection = function(key) {
|
sortSection = function(key) {
|
||||||
@ -448,7 +434,7 @@ Definition of the FRESHResume class.
|
|||||||
|
|
||||||
return FreshResume;
|
return FreshResume;
|
||||||
|
|
||||||
})();
|
})(AbstractResume);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -499,7 +485,7 @@ Definition of the FRESHResume class.
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (Object.prototype.toString.call(obj) === '[object Array]') {
|
if (Object.prototype.toString.call(obj) === '[object Array]') {
|
||||||
return obj.forEach(function(elem) {
|
obj.forEach(function(elem) {
|
||||||
return replaceDatesInObject(elem);
|
return replaceDatesInObject(elem);
|
||||||
});
|
});
|
||||||
} else if (typeof obj === 'object') {
|
} else if (typeof obj === 'object') {
|
||||||
@ -509,19 +495,19 @@ Definition of the FRESHResume class.
|
|||||||
Object.keys(obj).forEach(function(key) {
|
Object.keys(obj).forEach(function(key) {
|
||||||
return replaceDatesInObject(obj[key]);
|
return replaceDatesInObject(obj[key]);
|
||||||
});
|
});
|
||||||
return ['start', 'end', 'date'].forEach(function(val) {
|
['start', 'end', 'date'].forEach(function(val) {
|
||||||
if ((obj[val] !== void 0) && (!obj.safe || !obj.safe[val])) {
|
if ((obj[val] !== void 0) && (!obj.safe || !obj.safe[val])) {
|
||||||
obj.safe = obj.safe || {};
|
obj.safe = obj.safe || {};
|
||||||
obj.safe[val] = _fmt(obj[val]);
|
obj.safe[val] = _fmt(obj[val]);
|
||||||
if (obj[val] && (val === 'start') && !obj.end) {
|
if (obj[val] && (val === 'start') && !obj.end) {
|
||||||
return obj.safe.end = _fmt('current');
|
obj.safe.end = _fmt('current');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return Object.keys(this).forEach(function(member) {
|
Object.keys(this).forEach(function(member) {
|
||||||
return replaceDatesInObject(that[member]);
|
replaceDatesInObject(that[member]);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
40
dist/core/jrs-resume.js
vendored
40
dist/core/jrs-resume.js
vendored
@ -6,7 +6,9 @@ Definition of the JRSResume class.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
var CONVERTER, FS, JRSResume, MD, PATH, _, _parseDates, extend, moment, validator;
|
var AbstractResume, CONVERTER, FS, JRSResume, MD, PATH, _, _parseDates, extend, moment, validator,
|
||||||
|
extend1 = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
|
||||||
|
hasProp = {}.hasOwnProperty;
|
||||||
|
|
||||||
FS = require('fs');
|
FS = require('fs');
|
||||||
|
|
||||||
@ -24,6 +26,8 @@ Definition of the JRSResume class.
|
|||||||
|
|
||||||
moment = require('moment');
|
moment = require('moment');
|
||||||
|
|
||||||
|
AbstractResume = require('./abstract-resume');
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A JRS resume or CV. JRS resumes are backed by JSON, and each JRSResume object
|
A JRS resume or CV. JRS resumes are backed by JSON, and each JRSResume object
|
||||||
@ -31,10 +35,14 @@ Definition of the JRSResume class.
|
|||||||
@class JRSResume
|
@class JRSResume
|
||||||
*/
|
*/
|
||||||
|
|
||||||
JRSResume = (function() {
|
JRSResume = (function(superClass) {
|
||||||
var clear, format;
|
var clear, format;
|
||||||
|
|
||||||
function JRSResume() {}
|
extend1(JRSResume, superClass);
|
||||||
|
|
||||||
|
function JRSResume() {
|
||||||
|
return JRSResume.__super__.constructor.apply(this, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Initialize the JSResume from file. */
|
/** Initialize the JSResume from file. */
|
||||||
@ -249,30 +257,8 @@ Definition of the JRSResume class.
|
|||||||
return ret;
|
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().
|
|
||||||
@returns The total duration of the sheet's work history, that is, the number
|
|
||||||
of years between the start date of the earliest job on the resume and the
|
|
||||||
*latest end date of all jobs in the work history*. This last condition is for
|
|
||||||
sheets that have overlapping jobs.
|
|
||||||
*/
|
|
||||||
|
|
||||||
JRSResume.prototype.duration = function(unit) {
|
JRSResume.prototype.duration = function(unit) {
|
||||||
var careerLast, careerStart;
|
return JRSResume.__super__.duration.call(this, 'work', 'startDate', 'endDate', unit);
|
||||||
unit = unit || 'years';
|
|
||||||
if (this.work && this.work.length) {
|
|
||||||
careerStart = this.work[this.work.length - 1].safeStartDate;
|
|
||||||
if ((typeof careerStart === 'string' || careerStart instanceof String) && !careerStart.trim()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
careerLast = _.max(this.work, function(w) {
|
|
||||||
return w.safeEndDate.unix();
|
|
||||||
}).safeEndDate;
|
|
||||||
return careerLast.diff(careerStart, unit);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -372,7 +358,7 @@ Definition of the JRSResume class.
|
|||||||
|
|
||||||
return JRSResume;
|
return JRSResume;
|
||||||
|
|
||||||
})();
|
})(AbstractResume);
|
||||||
|
|
||||||
|
|
||||||
/** Get the default (empty) sheet. */
|
/** Get the default (empty) sheet. */
|
||||||
|
26
dist/core/jrs-theme.js
vendored
26
dist/core/jrs-theme.js
vendored
@ -6,7 +6,7 @@ Definition of the JRSTheme class.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
var JRSTheme, PATH, _, getFormat, parsePath, pathExists;
|
var JRSTheme, PATH, _, parsePath, pathExists;
|
||||||
|
|
||||||
_ = require('underscore');
|
_ = require('underscore');
|
||||||
|
|
||||||
@ -25,17 +25,13 @@ Definition of the JRSTheme class.
|
|||||||
JRSTheme = (function() {
|
JRSTheme = (function() {
|
||||||
function JRSTheme() {}
|
function JRSTheme() {}
|
||||||
|
|
||||||
return JRSTheme;
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
({
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Open and parse the specified theme.
|
Open and parse the specified theme.
|
||||||
@method open
|
@method open
|
||||||
*/
|
*/
|
||||||
open: function(thFolder) {
|
|
||||||
|
JRSTheme.prototype.open = function(thFolder) {
|
||||||
var pathInfo, pkgJsonPath, thApi, thPkg;
|
var pathInfo, pkgJsonPath, thApi, thPkg;
|
||||||
this.folder = thFolder;
|
this.folder = thFolder;
|
||||||
pathInfo = parsePath(thFolder);
|
pathInfo = parsePath(thFolder);
|
||||||
@ -78,26 +74,32 @@ Definition of the JRSTheme class.
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
},
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Determine if the theme supports the output format.
|
Determine if the theme supports the output format.
|
||||||
@method hasFormat
|
@method hasFormat
|
||||||
*/
|
*/
|
||||||
hasFormat: function(fmt) {
|
|
||||||
|
JRSTheme.prototype.hasFormat = function(fmt) {
|
||||||
return _.has(this.formats, fmt);
|
return _.has(this.formats, fmt);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Return the requested output format.
|
Return the requested output format.
|
||||||
@method getFormat
|
@method getFormat
|
||||||
*/
|
*/
|
||||||
});
|
|
||||||
|
|
||||||
getFormat = function(fmt) {
|
JRSTheme.prototype.getFormat = function(fmt) {
|
||||||
return this.formats[fmt];
|
return this.formats[fmt];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return JRSTheme;
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
module.exports = JRSTheme;
|
module.exports = JRSTheme;
|
||||||
|
|
||||||
}).call(this);
|
}).call(this);
|
||||||
|
5
dist/renderers/jrs-generator.js
vendored
5
dist/renderers/jrs-generator.js
vendored
@ -36,10 +36,11 @@ Definition of the JRSGenerator class.
|
|||||||
generate: function(json, jst, format, cssInfo, opts, theme) {
|
generate: function(json, jst, format, cssInfo, opts, theme) {
|
||||||
var org, rezHtml, turnoff;
|
var org, rezHtml, turnoff;
|
||||||
turnoff = ['log', 'error', 'dir'];
|
turnoff = ['log', 'error', 'dir'];
|
||||||
org = turnoff.map(c)(function() {
|
org = turnoff.map(function(c) {
|
||||||
var ret;
|
var ret;
|
||||||
ret = console[c];
|
ret = console[c];
|
||||||
return console[c] = function() {};
|
console[c] = function() {};
|
||||||
|
return ret;
|
||||||
});
|
});
|
||||||
rezHtml = theme.render(json.harden());
|
rezHtml = theme.render(json.harden());
|
||||||
turnoff.forEach(function(c, idx) {
|
turnoff.forEach(function(c, idx) {
|
||||||
|
9
dist/verbs/analyze.js
vendored
9
dist/verbs/analyze.js
vendored
@ -26,14 +26,7 @@ Implementation of the 'analyze' verb for HackMyResume.
|
|||||||
|
|
||||||
AnalyzeVerb = module.exports = Verb.extend({
|
AnalyzeVerb = module.exports = Verb.extend({
|
||||||
init: function() {
|
init: function() {
|
||||||
return this._super('analyze');
|
return this._super('analyze', analyze);
|
||||||
},
|
|
||||||
invoke: function() {
|
|
||||||
this.stat(HMEVENT.begin, {
|
|
||||||
cmd: 'analyze'
|
|
||||||
});
|
|
||||||
analyze.apply(this, arguments);
|
|
||||||
return this.stat(HMEVENT.end);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
41
dist/verbs/build.js
vendored
41
dist/verbs/build.js
vendored
@ -74,18 +74,7 @@ Implementation of the 'build' verb for HackMyResume.
|
|||||||
|
|
||||||
/** Create a new build verb. */
|
/** Create a new build verb. */
|
||||||
init: function() {
|
init: function() {
|
||||||
return this._super('build');
|
return this._super('build', build);
|
||||||
},
|
|
||||||
|
|
||||||
/** Invoke the Build command. */
|
|
||||||
invoke: function() {
|
|
||||||
var ret;
|
|
||||||
this.stat(HMEVENT.begin, {
|
|
||||||
cmd: 'build'
|
|
||||||
});
|
|
||||||
ret = build.apply(this, arguments);
|
|
||||||
this.stat(HMEVENT.end);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -95,16 +84,16 @@ Implementation of the 'build' verb for HackMyResume.
|
|||||||
theme file, generate 0..N resumes in the desired formats.
|
theme file, generate 0..N resumes in the desired formats.
|
||||||
@param src Path to the source JSON resume file: "rez/resume.json".
|
@param src Path to the source JSON resume file: "rez/resume.json".
|
||||||
@param dst An array of paths to the target resume file(s).
|
@param dst An array of paths to the target resume file(s).
|
||||||
@param theme Friendly name of the resume theme. Defaults to "modern".
|
@param opts Generation options.
|
||||||
@param logger Optional logging override.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
build = function(src, dst, opts) {
|
build = function(src, dst, opts) {
|
||||||
var ex, inv, isFRESH, mixed, newEx, orgFormat, rez, sheetObjects, sheets, tFolder, targets, theme, toFormat;
|
var ex, inv, isFRESH, mixed, newEx, orgFormat, problemSheets, rez, sheetObjects, sheets, tFolder, targets, theme, toFormat;
|
||||||
if (!src || !src.length) {
|
if (!src || !src.length) {
|
||||||
this.err(HMSTATUS.resumeNotFound, {
|
this.err(HMSTATUS.resumeNotFound, {
|
||||||
quit: true
|
quit: true
|
||||||
});
|
});
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
prep(src, dst, opts);
|
prep(src, dst, opts);
|
||||||
sheetObjects = ResumeFactory.load(src, {
|
sheetObjects = ResumeFactory.load(src, {
|
||||||
@ -115,9 +104,12 @@ Implementation of the 'build' verb for HackMyResume.
|
|||||||
sort: _opts.sort
|
sort: _opts.sort
|
||||||
}
|
}
|
||||||
}, this);
|
}, this);
|
||||||
if (!sheetObjects || _.some(sheetObjects, function(so) {
|
problemSheets = _.filter(sheetObjects, function(so) {
|
||||||
return so.fluenterror;
|
return so.fluenterror;
|
||||||
})) {
|
});
|
||||||
|
if (problemSheets && problemSheets.length) {
|
||||||
|
problemSheets[0].quit = true;
|
||||||
|
this.err(problemSheets[0].fluenterror, problemSheets[0]);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
sheets = sheetObjects.map(function(r) {
|
sheets = sheetObjects.map(function(r) {
|
||||||
@ -130,12 +122,14 @@ Implementation of the 'build' verb for HackMyResume.
|
|||||||
try {
|
try {
|
||||||
tFolder = verifyTheme.call(this, _opts.theme);
|
tFolder = verifyTheme.call(this, _opts.theme);
|
||||||
theme = _opts.themeObj = loadTheme(tFolder);
|
theme = _opts.themeObj = loadTheme(tFolder);
|
||||||
|
addFreebieFormats(theme);
|
||||||
} catch (_error) {
|
} catch (_error) {
|
||||||
ex = _error;
|
ex = _error;
|
||||||
newEx = {
|
newEx = {
|
||||||
fluenterror: HMSTATUS.themeLoad,
|
fluenterror: HMSTATUS.themeLoad,
|
||||||
inner: ex,
|
inner: ex,
|
||||||
attempted: _opts.theme
|
attempted: _opts.theme,
|
||||||
|
quit: true
|
||||||
};
|
};
|
||||||
this.err(HMSTATUS.themeLoad, newEx);
|
this.err(HMSTATUS.themeLoad, newEx);
|
||||||
return null;
|
return null;
|
||||||
@ -147,8 +141,10 @@ Implementation of the 'build' verb for HackMyResume.
|
|||||||
if (inv && inv.length) {
|
if (inv && inv.length) {
|
||||||
this.err(HMSTATUS.invalidFormat, {
|
this.err(HMSTATUS.invalidFormat, {
|
||||||
data: inv,
|
data: inv,
|
||||||
theme: theme
|
theme: theme,
|
||||||
|
quit: true
|
||||||
});
|
});
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
rez = null;
|
rez = null;
|
||||||
if (sheets.length > 1) {
|
if (sheets.length > 1) {
|
||||||
@ -186,7 +182,6 @@ Implementation of the 'build' verb for HackMyResume.
|
|||||||
fmt: toFormat
|
fmt: toFormat
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
addFreebieFormats(theme);
|
|
||||||
this.stat(HMEVENT.applyTheme, {
|
this.stat(HMEVENT.applyTheme, {
|
||||||
r: rez,
|
r: rez,
|
||||||
theme: theme
|
theme: theme
|
||||||
@ -247,12 +242,12 @@ Implementation of the 'build' verb for HackMyResume.
|
|||||||
fmt: targInfo.fmt.outFormat,
|
fmt: targInfo.fmt.outFormat,
|
||||||
file: PATH.relative(process.cwd(), f)
|
file: PATH.relative(process.cwd(), f)
|
||||||
});
|
});
|
||||||
|
_opts.targets = finished;
|
||||||
if (targInfo.fmt.files && targInfo.fmt.files.length) {
|
if (targInfo.fmt.files && targInfo.fmt.files.length) {
|
||||||
theFormat = _fmts.filter(function(fmt) {
|
theFormat = _fmts.filter(function(fmt) {
|
||||||
return fmt.name === targInfo.fmt.outFormat;
|
return fmt.name === targInfo.fmt.outFormat;
|
||||||
})[0];
|
})[0];
|
||||||
MKDIRP.sync(PATH.dirname(f));
|
MKDIRP.sync(PATH.dirname(f));
|
||||||
_opts.targets = finished;
|
|
||||||
ret = theFormat.gen.generate(_rezObj, f, _opts);
|
ret = theFormat.gen.generate(_rezObj, f, _opts);
|
||||||
} else {
|
} else {
|
||||||
theFormat = _fmts.filter(function(fmt) {
|
theFormat = _fmts.filter(function(fmt) {
|
||||||
@ -284,9 +279,7 @@ Implementation of the 'build' verb for HackMyResume.
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/** Ensure that user-specified outputs/targets are valid. */
|
||||||
Ensure that user-specified outputs/targets are valid.
|
|
||||||
*/
|
|
||||||
|
|
||||||
verifyOutputs = function(targets, theme) {
|
verifyOutputs = function(targets, theme) {
|
||||||
this.stat(HMEVENT.verifyOutputs, {
|
this.stat(HMEVENT.verifyOutputs, {
|
||||||
|
9
dist/verbs/convert.js
vendored
9
dist/verbs/convert.js
vendored
@ -22,14 +22,7 @@ Implementation of the 'convert' verb for HackMyResume.
|
|||||||
|
|
||||||
ConvertVerb = module.exports = Verb.extend({
|
ConvertVerb = module.exports = Verb.extend({
|
||||||
init: function() {
|
init: function() {
|
||||||
return this._super('convert');
|
return this._super('convert', convert);
|
||||||
},
|
|
||||||
invoke: function() {
|
|
||||||
this.stat(HMEVENT.begin, {
|
|
||||||
cmd: 'convert'
|
|
||||||
});
|
|
||||||
convert.apply(this, arguments);
|
|
||||||
return this.stat(HMEVENT.end);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
11
dist/verbs/create.js
vendored
11
dist/verbs/create.js
vendored
@ -24,16 +24,7 @@ Implementation of the 'create' verb for HackMyResume.
|
|||||||
|
|
||||||
CreateVerb = module.exports = Verb.extend({
|
CreateVerb = module.exports = Verb.extend({
|
||||||
init: function() {
|
init: function() {
|
||||||
return this._super('new');
|
return this._super('new', create);
|
||||||
},
|
|
||||||
invoke: function() {
|
|
||||||
this.stat(HMEVENT.begin, {
|
|
||||||
cmd: 'create'
|
|
||||||
});
|
|
||||||
create.apply(this, arguments);
|
|
||||||
this.stat(HMEVENT.begin, {
|
|
||||||
cmd: 'convert'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
9
dist/verbs/peek.js
vendored
9
dist/verbs/peek.js
vendored
@ -22,14 +22,7 @@ Implementation of the 'peek' verb for HackMyResume.
|
|||||||
|
|
||||||
PeekVerb = module.exports = Verb.extend({
|
PeekVerb = module.exports = Verb.extend({
|
||||||
init: function() {
|
init: function() {
|
||||||
return this._super('peek');
|
return this._super('peek', peek);
|
||||||
},
|
|
||||||
invoke: function() {
|
|
||||||
this.stat(HMEVENT.begin, {
|
|
||||||
cmd: 'peek'
|
|
||||||
});
|
|
||||||
peek.apply(this, arguments);
|
|
||||||
return this.stat(HMEVENT.end);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
12
dist/verbs/validate.js
vendored
12
dist/verbs/validate.js
vendored
@ -31,16 +31,7 @@ Implementation of the 'validate' verb for HackMyResume.
|
|||||||
|
|
||||||
ValidateVerb = module.exports = Verb.extend({
|
ValidateVerb = module.exports = Verb.extend({
|
||||||
init: function() {
|
init: function() {
|
||||||
return this._super('validate');
|
return this._super('validate', validate);
|
||||||
},
|
|
||||||
invoke: function() {
|
|
||||||
var ret;
|
|
||||||
this.stat(HMEVENT.begin, {
|
|
||||||
cmd: 'validate'
|
|
||||||
});
|
|
||||||
ret = validate.apply(this, arguments);
|
|
||||||
this.stat(HMEVENT.end);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -104,7 +95,6 @@ Implementation of the 'validate' verb for HackMyResume.
|
|||||||
shouldExit: true
|
shouldExit: true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
console.log('1111');
|
|
||||||
return ret;
|
return ret;
|
||||||
}, this);
|
}, this);
|
||||||
};
|
};
|
||||||
|
18
dist/verbs/verb.js
vendored
18
dist/verbs/verb.js
vendored
@ -6,12 +6,14 @@ Definition of the Verb class.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
var Class, EVENTS, Verb;
|
var Class, EVENTS, HMEVENT, Verb;
|
||||||
|
|
||||||
Class = require('../utils/class');
|
Class = require('../utils/class');
|
||||||
|
|
||||||
EVENTS = require('events');
|
EVENTS = require('events');
|
||||||
|
|
||||||
|
HMEVENT = require('../core/event-codes');
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
An instantiation of a HackMyResume command.
|
An instantiation of a HackMyResume command.
|
||||||
@ -21,9 +23,21 @@ Definition of the Verb class.
|
|||||||
Verb = module.exports = Class.extend({
|
Verb = module.exports = Class.extend({
|
||||||
|
|
||||||
/** Constructor. Automatically called at creation. */
|
/** Constructor. Automatically called at creation. */
|
||||||
init: function(moniker) {
|
init: function(moniker, workhorse) {
|
||||||
this.moniker = moniker;
|
this.moniker = moniker;
|
||||||
this.emitter = new EVENTS.EventEmitter();
|
this.emitter = new EVENTS.EventEmitter();
|
||||||
|
this.workhorse = workhorse;
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Invoke the command. */
|
||||||
|
invoke: function() {
|
||||||
|
var ret;
|
||||||
|
this.stat(HMEVENT.begin, {
|
||||||
|
cmd: this.moniker
|
||||||
|
});
|
||||||
|
ret = this.workhorse.apply(this, arguments);
|
||||||
|
this.stat(HMEVENT.end);
|
||||||
|
return ret;
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Forward subscriptions to the event emitter. */
|
/** Forward subscriptions to the event emitter. */
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hackmyresume",
|
"name": "hackmyresume",
|
||||||
"version": "1.7.3",
|
"version": "1.7.4",
|
||||||
"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",
|
||||||
|
53
src/core/abstract-resume.coffee
Normal file
53
src/core/abstract-resume.coffee
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
###*
|
||||||
|
Definition of the AbstractResume class.
|
||||||
|
@license MIT. See LICENSE.md for details.
|
||||||
|
@module core/abstract-resume
|
||||||
|
###
|
||||||
|
|
||||||
|
_ = require 'underscore'
|
||||||
|
__ = require 'lodash'
|
||||||
|
FluentDate = require('./fluent-date')
|
||||||
|
|
||||||
|
class AbstractResume
|
||||||
|
|
||||||
|
###*
|
||||||
|
Compute the total duration of the work history.
|
||||||
|
@returns The total duration of the sheet's work history, that is, the number
|
||||||
|
of years between the start date of the earliest job on the resume and the
|
||||||
|
*latest end date of all jobs in the work history*. This last condition is for
|
||||||
|
sheets that have overlapping jobs.
|
||||||
|
###
|
||||||
|
duration: (collKey, startKey, endKey, unit) ->
|
||||||
|
unit = unit || 'years'
|
||||||
|
hist = __.get @, collKey
|
||||||
|
return 0 if !hist or !hist.length
|
||||||
|
|
||||||
|
# BEGIN CODE DUPLICATION --> src/inspectors/gap-inspector.coffee (TODO)
|
||||||
|
|
||||||
|
# Convert the candidate's employment history to an array of dates,
|
||||||
|
# where each element in the array is a start date or an end date of a
|
||||||
|
# job -- it doesn't matter which.
|
||||||
|
new_e = hist.map ( job ) ->
|
||||||
|
obj = _.pick( job, [startKey, endKey] )
|
||||||
|
# Synthesize an end date if this is a "current" gig
|
||||||
|
obj[endKey] = 'current' if !_.has obj, endKey
|
||||||
|
if obj && (obj[startKey] || obj[endKey])
|
||||||
|
obj = _.pairs obj
|
||||||
|
obj[0][1] = FluentDate.fmt( obj[0][1] )
|
||||||
|
if obj.length > 1
|
||||||
|
obj[1][1] = FluentDate.fmt( obj[1][1] )
|
||||||
|
obj
|
||||||
|
|
||||||
|
# Flatten the array, remove empties, and sort
|
||||||
|
new_e = _.filter _.flatten( new_e, true ), (v) ->
|
||||||
|
return v && v.length && v[0] && v[0].length
|
||||||
|
return 0 if !new_e or !new_e.length
|
||||||
|
new_e = _.sortBy new_e, ( elem ) -> return elem[1].unix()
|
||||||
|
|
||||||
|
# END CODE DUPLICATION
|
||||||
|
|
||||||
|
firstDate = _.first( new_e )[1];
|
||||||
|
lastDate = _.last( new_e )[1];
|
||||||
|
lastDate.diff firstDate, unit
|
||||||
|
|
||||||
|
module.exports = AbstractResume
|
@ -7,6 +7,7 @@ The HackMyResume date representation.
|
|||||||
|
|
||||||
|
|
||||||
moment = require 'moment'
|
moment = require 'moment'
|
||||||
|
require('../utils/string')
|
||||||
|
|
||||||
###*
|
###*
|
||||||
Create a FluentDate from a string or Moment date object. There are a few date
|
Create a FluentDate from a string or Moment date object. There are a few date
|
||||||
@ -30,6 +31,8 @@ class FluentDate
|
|||||||
constructor: (dt) ->
|
constructor: (dt) ->
|
||||||
@rep = this.fmt dt
|
@rep = this.fmt dt
|
||||||
|
|
||||||
|
@isCurrent: (dt) ->
|
||||||
|
!dt || (String.is(dt) and /^(present|now|current)$/.test(dt))
|
||||||
|
|
||||||
months = {}
|
months = {}
|
||||||
abbr = {}
|
abbr = {}
|
||||||
@ -56,16 +59,7 @@ FluentDate.fmt = ( dt, throws ) ->
|
|||||||
else if /^\s*\d{4}\s*$/.test(dt) # "2015"
|
else if /^\s*\d{4}\s*$/.test(dt) # "2015"
|
||||||
return moment dt, 'YYYY'
|
return moment dt, 'YYYY'
|
||||||
else if /^\s*$/.test(dt) # "", " "
|
else if /^\s*$/.test(dt) # "", " "
|
||||||
defTime =
|
return moment()
|
||||||
isNull: true
|
|
||||||
isBefore: ( other ) ->
|
|
||||||
if other and !other.isNull then true else false
|
|
||||||
isAfter: ( other ) ->
|
|
||||||
if other and !other.isNull then false else false
|
|
||||||
unix: () -> 0
|
|
||||||
format: () -> ''
|
|
||||||
diff: () -> 0
|
|
||||||
return defTime
|
|
||||||
else
|
else
|
||||||
mt = moment dt
|
mt = moment dt
|
||||||
if mt.isValid()
|
if mt.isValid()
|
||||||
|
@ -17,6 +17,8 @@ XML = require 'xml-escape'
|
|||||||
MD = require 'marked'
|
MD = require 'marked'
|
||||||
CONVERTER = require 'fresh-jrs-converter'
|
CONVERTER = require 'fresh-jrs-converter'
|
||||||
JRSResume = require './jrs-resume'
|
JRSResume = require './jrs-resume'
|
||||||
|
FluentDate = require './fluent-date'
|
||||||
|
AbstractResume = require './abstract-resume'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -25,7 +27,7 @@ A FRESH resume or CV. FRESH resumes are backed by JSON, and each FreshResume
|
|||||||
object is an instantiation of that JSON decorated with utility methods.
|
object is an instantiation of that JSON decorated with utility methods.
|
||||||
@constructor
|
@constructor
|
||||||
###
|
###
|
||||||
class FreshResume
|
class FreshResume extends AbstractResume
|
||||||
|
|
||||||
###* Initialize the FreshResume from file. ###
|
###* Initialize the FreshResume from file. ###
|
||||||
open: ( file, opts ) ->
|
open: ( file, opts ) ->
|
||||||
@ -306,28 +308,8 @@ class FreshResume
|
|||||||
ret
|
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().
|
|
||||||
@returns The total duration of the sheet's work history, that is, the number
|
|
||||||
of years between the start date of the earliest job on the resume and the
|
|
||||||
*latest end date of all jobs in the work history*. This last condition is for
|
|
||||||
sheets that have overlapping jobs.
|
|
||||||
###
|
|
||||||
duration: (unit) ->
|
duration: (unit) ->
|
||||||
unit = unit || 'years'
|
super('employment.history', 'start', 'end', unit)
|
||||||
empHist = __.get(this, 'employment.history')
|
|
||||||
if empHist && empHist.length
|
|
||||||
firstJob = _.last( empHist )
|
|
||||||
careerStart = if firstJob.start then firstJob.safe.start else ''
|
|
||||||
if ((typeof careerStart == 'string' || careerStart instanceof String) && !careerStart.trim())
|
|
||||||
return 0
|
|
||||||
careerLast = _.max empHist, ( w ) ->
|
|
||||||
return if w.safe && w.safe.end then w.safe.end.unix() else moment().unix()
|
|
||||||
return careerLast.safe.end.diff careerStart, unit
|
|
||||||
0
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###*
|
###*
|
||||||
@ -337,9 +319,9 @@ class FreshResume
|
|||||||
sort: () ->
|
sort: () ->
|
||||||
|
|
||||||
byDateDesc = (a,b) ->
|
byDateDesc = (a,b) ->
|
||||||
if ( a.safe.start.isBefore(b.safe.start) )
|
if a.safe.start.isBefore(b.safe.start)
|
||||||
then 1
|
then 1
|
||||||
else ( a.safe.start.isAfter(b.safe.start) && -1 ) || 0
|
else ( if a.safe.start.isAfter(b.safe.start) then -1 else 0 )
|
||||||
|
|
||||||
sortSection = ( key ) ->
|
sortSection = ( key ) ->
|
||||||
ar = __.get this, key
|
ar = __.get this, key
|
||||||
@ -352,10 +334,6 @@ class FreshResume
|
|||||||
sortSection 'service.history'
|
sortSection 'service.history'
|
||||||
sortSection 'projects'
|
sortSection 'projects'
|
||||||
|
|
||||||
# this.awards && this.awards.sort( function(a, b) {
|
|
||||||
# return( a.safeDate.isBefore(b.safeDate) ) ? 1
|
|
||||||
# : ( a.safeDate.isAfter(b.safeDate) && -1 ) || 0;
|
|
||||||
# });
|
|
||||||
@writing && @writing.sort (a, b) ->
|
@writing && @writing.sort (a, b) ->
|
||||||
if a.safe.date.isBefore b.safe.date
|
if a.safe.date.isBefore b.safe.date
|
||||||
then 1
|
then 1
|
||||||
@ -400,6 +378,7 @@ _parseDates = () ->
|
|||||||
return if !obj
|
return if !obj
|
||||||
if Object.prototype.toString.call( obj ) == '[object Array]'
|
if Object.prototype.toString.call( obj ) == '[object Array]'
|
||||||
obj.forEach (elem) -> replaceDatesInObject( elem )
|
obj.forEach (elem) -> replaceDatesInObject( elem )
|
||||||
|
return
|
||||||
else if typeof obj == 'object'
|
else if typeof obj == 'object'
|
||||||
if obj._isAMomentObject || obj.safe
|
if obj._isAMomentObject || obj.safe
|
||||||
return
|
return
|
||||||
@ -410,8 +389,12 @@ _parseDates = () ->
|
|||||||
obj.safe[ val ] = _fmt obj[val]
|
obj.safe[ val ] = _fmt obj[val]
|
||||||
if obj[val] && (val == 'start') && !obj.end
|
if obj[val] && (val == 'start') && !obj.end
|
||||||
obj.safe.end = _fmt 'current'
|
obj.safe.end = _fmt 'current'
|
||||||
|
return
|
||||||
Object.keys( this ).forEach (member) -> replaceDatesInObject(that[member])
|
return
|
||||||
|
Object.keys( this ).forEach (member) ->
|
||||||
|
replaceDatesInObject(that[member])
|
||||||
|
return
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ PATH = require('path')
|
|||||||
MD = require('marked')
|
MD = require('marked')
|
||||||
CONVERTER = require('fresh-jrs-converter')
|
CONVERTER = require('fresh-jrs-converter')
|
||||||
moment = require('moment')
|
moment = require('moment')
|
||||||
|
AbstractResume = require('./abstract-resume')
|
||||||
|
|
||||||
|
|
||||||
###*
|
###*
|
||||||
@ -22,7 +22,7 @@ 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.
|
is an instantiation of that JSON decorated with utility methods.
|
||||||
@class JRSResume
|
@class JRSResume
|
||||||
###
|
###
|
||||||
class JRSResume
|
class JRSResume extends AbstractResume
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -203,25 +203,8 @@ class JRSResume
|
|||||||
ret
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
duration: (unit) ->
|
||||||
###*
|
super('work', 'startDate', 'endDate', unit)
|
||||||
Calculate the total duration of the sheet. Assumes this.work has been sorted
|
|
||||||
by start date descending, perhaps via a call to Sheet.sort().
|
|
||||||
@returns The total duration of the sheet's work history, that is, the number
|
|
||||||
of years between the start date of the earliest job on the resume and the
|
|
||||||
*latest end date of all jobs in the work history*. This last condition is for
|
|
||||||
sheets that have overlapping jobs.
|
|
||||||
###
|
|
||||||
duration: ( unit ) ->
|
|
||||||
unit = unit || 'years';
|
|
||||||
if this.work && this.work.length
|
|
||||||
careerStart = this.work[ this.work.length - 1].safeStartDate
|
|
||||||
if (typeof careerStart == 'string' || careerStart instanceof String) && !careerStart.trim()
|
|
||||||
return 0
|
|
||||||
careerLast = _.max( this.work, ( w ) -> w.safeEndDate.unix() ).safeEndDate;
|
|
||||||
return careerLast.diff careerStart, unit
|
|
||||||
0
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###*
|
###*
|
||||||
|
@ -20,11 +20,12 @@ The JRSTheme class is a representation of a JSON Resume theme asset.
|
|||||||
class JRSTheme
|
class JRSTheme
|
||||||
|
|
||||||
|
|
||||||
###*
|
|
||||||
Open and parse the specified theme.
|
###*
|
||||||
@method open
|
Open and parse the specified theme.
|
||||||
###
|
@method open
|
||||||
open: ( thFolder ) ->
|
###
|
||||||
|
open: ( thFolder ) ->
|
||||||
|
|
||||||
@folder = thFolder
|
@folder = thFolder
|
||||||
|
|
||||||
@ -69,19 +70,19 @@ open: ( thFolder ) ->
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
###*
|
###*
|
||||||
Determine if the theme supports the output format.
|
Determine if the theme supports the output format.
|
||||||
@method hasFormat
|
@method hasFormat
|
||||||
###
|
###
|
||||||
hasFormat: ( fmt ) -> _.has this.formats, fmt
|
hasFormat: ( fmt ) -> _.has this.formats, fmt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###*
|
###*
|
||||||
Return the requested output format.
|
Return the requested output format.
|
||||||
@method getFormat
|
@method getFormat
|
||||||
###
|
###
|
||||||
getFormat = ( fmt ) -> @formats[ fmt ]
|
getFormat: ( fmt ) -> @formats[ fmt ]
|
||||||
|
|
||||||
|
|
||||||
module.exports = JRSTheme;
|
module.exports = JRSTheme;
|
||||||
|
@ -25,9 +25,10 @@ JRSGenerator = module.exports =
|
|||||||
|
|
||||||
# Disable JRS theme chatter (console.log, console.error, etc.)
|
# Disable JRS theme chatter (console.log, console.error, etc.)
|
||||||
turnoff = ['log', 'error', 'dir'];
|
turnoff = ['log', 'error', 'dir'];
|
||||||
org = turnoff.map(c) ->
|
org = turnoff.map (c) ->
|
||||||
ret = console[c]
|
ret = console[c]
|
||||||
console[c] = () ->
|
console[c] = () ->
|
||||||
|
ret
|
||||||
|
|
||||||
# Freeze and render
|
# Freeze and render
|
||||||
rezHtml = theme.render json.harden()
|
rezHtml = theme.render json.harden()
|
||||||
|
@ -19,12 +19,7 @@ chalk = require('chalk')
|
|||||||
|
|
||||||
AnalyzeVerb = module.exports = Verb.extend
|
AnalyzeVerb = module.exports = Verb.extend
|
||||||
|
|
||||||
init: -> @._super 'analyze'
|
init: -> @_super 'analyze', analyze
|
||||||
|
|
||||||
invoke: ->
|
|
||||||
@.stat HMEVENT.begin, { cmd: 'analyze' }
|
|
||||||
analyze.apply @, arguments
|
|
||||||
@.stat HMEVENT.end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,14 +42,7 @@ loadTheme = null
|
|||||||
BuildVerb = module.exports = Verb.extend
|
BuildVerb = module.exports = Verb.extend
|
||||||
|
|
||||||
###* Create a new build verb. ###
|
###* Create a new build verb. ###
|
||||||
init: () -> @._super 'build'
|
init: () -> @_super 'build', build
|
||||||
|
|
||||||
###* Invoke the Build command. ###
|
|
||||||
invoke: ->
|
|
||||||
@stat HMEVENT.begin, { cmd: 'build' }
|
|
||||||
ret = build.apply @, arguments
|
|
||||||
@stat HMEVENT.end
|
|
||||||
return ret
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -58,13 +51,13 @@ Given a source resume in FRESH or JRS format, a destination resume path, and a
|
|||||||
theme file, generate 0..N resumes in the desired formats.
|
theme file, generate 0..N resumes in the desired formats.
|
||||||
@param src Path to the source JSON resume file: "rez/resume.json".
|
@param src Path to the source JSON resume file: "rez/resume.json".
|
||||||
@param dst An array of paths to the target resume file(s).
|
@param dst An array of paths to the target resume file(s).
|
||||||
@param theme Friendly name of the resume theme. Defaults to "modern".
|
@param opts Generation options.
|
||||||
@param logger Optional logging override.
|
|
||||||
###
|
###
|
||||||
build = ( src, dst, opts ) ->
|
build = ( src, dst, opts ) ->
|
||||||
|
|
||||||
if !src || !src.length
|
if !src || !src.length
|
||||||
@err HMSTATUS.resumeNotFound, { quit: true }
|
@err HMSTATUS.resumeNotFound, quit: true
|
||||||
|
return null
|
||||||
|
|
||||||
prep src, dst, opts
|
prep src, dst, opts
|
||||||
|
|
||||||
@ -74,10 +67,14 @@ build = ( src, dst, opts ) ->
|
|||||||
}, @);
|
}, @);
|
||||||
|
|
||||||
# Explicit check for any resume loading errors...
|
# Explicit check for any resume loading errors...
|
||||||
if !sheetObjects || _.some( sheetObjects, (so) -> return so.fluenterror )
|
problemSheets = _.filter( sheetObjects, (so) -> return so.fluenterror )
|
||||||
|
if( problemSheets && problemSheets.length )
|
||||||
|
problemSheets[0].quit = true
|
||||||
|
@err problemSheets[0].fluenterror, problemSheets[0]
|
||||||
return null
|
return null
|
||||||
|
|
||||||
sheets = sheetObjects.map((r) -> return r.json )
|
# Get the collection of raw JSON sheets
|
||||||
|
sheets = sheetObjects.map (r) -> r.json
|
||||||
|
|
||||||
# Load the theme...
|
# Load the theme...
|
||||||
theme = null
|
theme = null
|
||||||
@ -85,12 +82,14 @@ build = ( src, dst, opts ) ->
|
|||||||
try
|
try
|
||||||
tFolder = verifyTheme.call @, _opts.theme
|
tFolder = verifyTheme.call @, _opts.theme
|
||||||
theme = _opts.themeObj = loadTheme tFolder
|
theme = _opts.themeObj = loadTheme tFolder
|
||||||
|
addFreebieFormats theme
|
||||||
catch ex
|
catch ex
|
||||||
newEx =
|
newEx =
|
||||||
fluenterror: HMSTATUS.themeLoad
|
fluenterror: HMSTATUS.themeLoad
|
||||||
inner: ex
|
inner: ex
|
||||||
attempted: _opts.theme
|
attempted: _opts.theme
|
||||||
this.err HMSTATUS.themeLoad, newEx
|
quit: true
|
||||||
|
@err HMSTATUS.themeLoad, newEx
|
||||||
return null
|
return null
|
||||||
|
|
||||||
@stat HMEVENT.afterTheme, { theme: theme }
|
@stat HMEVENT.afterTheme, { theme: theme }
|
||||||
@ -98,7 +97,8 @@ build = ( src, dst, opts ) ->
|
|||||||
# Check for invalid outputs...
|
# Check for invalid outputs...
|
||||||
inv = verifyOutputs.call @, dst, theme
|
inv = verifyOutputs.call @, dst, theme
|
||||||
if inv && inv.length
|
if inv && inv.length
|
||||||
@err HMSTATUS.invalidFormat, { data: inv, theme: theme }
|
@err HMSTATUS.invalidFormat, data: inv, theme: theme, quit: true
|
||||||
|
return null
|
||||||
|
|
||||||
## Merge input resumes, yielding a single source resume.
|
## Merge input resumes, yielding a single source resume.
|
||||||
rez = null
|
rez = null
|
||||||
@ -107,7 +107,7 @@ build = ( src, dst, opts ) ->
|
|||||||
mixed = _.any sheets, (s) -> return if isFRESH then s.basics else !s.basics
|
mixed = _.any sheets, (s) -> return if isFRESH then s.basics else !s.basics
|
||||||
@stat HMEVENT.beforeMerge, { f: _.clone(sheetObjects), mixed: mixed }
|
@stat HMEVENT.beforeMerge, { f: _.clone(sheetObjects), mixed: mixed }
|
||||||
if mixed
|
if mixed
|
||||||
this.err HMSTATUS.mixedMerge
|
@err HMSTATUS.mixedMerge
|
||||||
|
|
||||||
rez = _.reduceRight sheets, ( a, b, idx ) ->
|
rez = _.reduceRight sheets, ( a, b, idx ) ->
|
||||||
extend( true, b, a )
|
extend( true, b, a )
|
||||||
@ -124,8 +124,7 @@ build = ( src, dst, opts ) ->
|
|||||||
rez = RConverter[ 'to' + toFormat ]( rez );
|
rez = RConverter[ 'to' + toFormat ]( rez );
|
||||||
@stat HMEVENT.afterInlineConvert, { file: sheetObjects[0].file, fmt: toFormat }
|
@stat HMEVENT.afterInlineConvert, { file: sheetObjects[0].file, fmt: toFormat }
|
||||||
|
|
||||||
# Add freebie formats to the theme
|
# Announce the theme
|
||||||
addFreebieFormats theme
|
|
||||||
@stat HMEVENT.applyTheme, { r: rez, theme: theme }
|
@stat HMEVENT.applyTheme, { r: rez, theme: theme }
|
||||||
|
|
||||||
# Load the resume into a FRESHResume or JRSResume object
|
# Load the resume into a FRESHResume or JRSResume object
|
||||||
@ -195,13 +194,14 @@ single = ( targInfo, theme, finished ) ->
|
|||||||
fmt: targInfo.fmt.outFormat
|
fmt: targInfo.fmt.outFormat
|
||||||
file: PATH.relative(process.cwd(), f)
|
file: PATH.relative(process.cwd(), f)
|
||||||
|
|
||||||
|
_opts.targets = finished;
|
||||||
|
|
||||||
# If targInfo.fmt.files exists, this format is backed by a document.
|
# If targInfo.fmt.files exists, this format is backed by a document.
|
||||||
# Fluent/FRESH themes are handled here.
|
# Fluent/FRESH themes are handled here.
|
||||||
if targInfo.fmt.files && targInfo.fmt.files.length
|
if targInfo.fmt.files && targInfo.fmt.files.length
|
||||||
theFormat = _fmts.filter(
|
theFormat = _fmts.filter(
|
||||||
(fmt) -> return fmt.name == targInfo.fmt.outFormat )[0];
|
(fmt) -> return fmt.name == targInfo.fmt.outFormat )[0];
|
||||||
MKDIRP.sync( PATH.dirname( f ) ); # Ensure dest folder exists;
|
MKDIRP.sync( PATH.dirname( f ) ); # Ensure dest folder exists;
|
||||||
_opts.targets = finished;
|
|
||||||
ret = theFormat.gen.generate( _rezObj, f, _opts );
|
ret = theFormat.gen.generate( _rezObj, f, _opts );
|
||||||
|
|
||||||
# Otherwise this is an ad-hoc format (JSON, YML, or PNG) that every theme
|
# Otherwise this is an ad-hoc format (JSON, YML, or PNG) that every theme
|
||||||
@ -235,11 +235,9 @@ single = ( targInfo, theme, finished ) ->
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
###*
|
###* Ensure that user-specified outputs/targets are valid. ###
|
||||||
Ensure that user-specified outputs/targets are valid.
|
|
||||||
###
|
|
||||||
verifyOutputs = ( targets, theme ) ->
|
verifyOutputs = ( targets, theme ) ->
|
||||||
@.stat HMEVENT.verifyOutputs, { targets: targets, theme: theme }
|
@stat HMEVENT.verifyOutputs, { targets: targets, theme: theme }
|
||||||
_.reject targets.map( ( t ) ->
|
_.reject targets.map( ( t ) ->
|
||||||
pathInfo = parsePath t
|
pathInfo = parsePath t
|
||||||
{
|
{
|
||||||
@ -247,6 +245,8 @@ verifyOutputs = ( targets, theme ) ->
|
|||||||
}),
|
}),
|
||||||
(t) -> t.format == 'all' || theme.hasFormat( t.format )
|
(t) -> t.format == 'all' || theme.hasFormat( t.format )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###*
|
###*
|
||||||
Reinforce the chosen theme with "freebie" formats provided by HackMyResume.
|
Reinforce the chosen theme with "freebie" formats provided by HackMyResume.
|
||||||
A "freebie" format is an output format such as JSON, YML, or PNG that can be
|
A "freebie" format is an output format such as JSON, YML, or PNG that can be
|
||||||
|
@ -17,12 +17,7 @@ HMEVENT = require('../core/event-codes');
|
|||||||
|
|
||||||
ConvertVerb = module.exports = Verb.extend
|
ConvertVerb = module.exports = Verb.extend
|
||||||
|
|
||||||
init: -> @._super 'convert'
|
init: -> @_super 'convert', convert
|
||||||
|
|
||||||
invoke: ->
|
|
||||||
@.stat HMEVENT.begin, { cmd: 'convert' }
|
|
||||||
convert.apply @, arguments
|
|
||||||
@.stat HMEVENT.end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,13 +17,8 @@ HMEVENT = require('../core/event-codes')
|
|||||||
|
|
||||||
CreateVerb = module.exports = Verb.extend
|
CreateVerb = module.exports = Verb.extend
|
||||||
|
|
||||||
init: -> @._super('new')
|
init: -> @_super 'new', create
|
||||||
|
|
||||||
invoke: ->
|
|
||||||
@.stat HMEVENT.begin, { cmd: 'create' }
|
|
||||||
create.apply @, arguments
|
|
||||||
@.stat HMEVENT.begin, { cmd: 'convert' }
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
###*
|
###*
|
||||||
|
@ -17,12 +17,7 @@ HMEVENT = require('../core/event-codes')
|
|||||||
|
|
||||||
PeekVerb = module.exports = Verb.extend
|
PeekVerb = module.exports = Verb.extend
|
||||||
|
|
||||||
init: -> @._super('peek')
|
init: -> @_super 'peek', peek
|
||||||
|
|
||||||
invoke: ->
|
|
||||||
@.stat HMEVENT.begin, { cmd: 'peek' }
|
|
||||||
peek.apply @, arguments
|
|
||||||
@.stat HMEVENT.end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,13 +21,7 @@ safeLoadJSON = require '../utils/safe-json-loader'
|
|||||||
###* An invokable resume validation command. ###
|
###* An invokable resume validation command. ###
|
||||||
ValidateVerb = module.exports = Verb.extend
|
ValidateVerb = module.exports = Verb.extend
|
||||||
|
|
||||||
init: -> @_super 'validate'
|
init: -> @_super 'validate', validate
|
||||||
|
|
||||||
invoke: ->
|
|
||||||
@stat HMEVENT.begin, { cmd: 'validate' }
|
|
||||||
ret = validate.apply @, arguments
|
|
||||||
@stat HMEVENT.end
|
|
||||||
return ret
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -85,8 +79,6 @@ validate = (sources, unused, opts) ->
|
|||||||
if opts.assert and !ret.isValid
|
if opts.assert and !ret.isValid
|
||||||
throw fluenterror: HMSTATUS.invalid, shouldExit: true
|
throw fluenterror: HMSTATUS.invalid, shouldExit: true
|
||||||
|
|
||||||
console.log '1111'
|
ret
|
||||||
|
|
||||||
return ret
|
|
||||||
|
|
||||||
, @
|
, @
|
||||||
|
@ -6,10 +6,10 @@ Definition of the Verb class.
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Use J. Resig's nifty class implementation
|
# Use J. Resig's nifty class implementation
|
||||||
Class = require '../utils/class'
|
Class = require '../utils/class'
|
||||||
EVENTS = require 'events'
|
EVENTS = require 'events'
|
||||||
|
HMEVENT = require '../core/event-codes'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -22,24 +22,34 @@ Verb = module.exports = Class.extend
|
|||||||
|
|
||||||
|
|
||||||
###* Constructor. Automatically called at creation. ###
|
###* Constructor. Automatically called at creation. ###
|
||||||
init: ( moniker ) ->
|
init: ( moniker, workhorse ) ->
|
||||||
@.moniker = moniker
|
@moniker = moniker
|
||||||
@.emitter = new EVENTS.EventEmitter()
|
@emitter = new EVENTS.EventEmitter()
|
||||||
|
@workhorse = workhorse
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
###* Invoke the command. ###
|
||||||
|
invoke: ->
|
||||||
|
@stat HMEVENT.begin, cmd: @moniker
|
||||||
|
ret = @workhorse.apply @, arguments
|
||||||
|
@stat HMEVENT.end
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###* Forward subscriptions to the event emitter. ###
|
###* Forward subscriptions to the event emitter. ###
|
||||||
on: ->
|
on: ->
|
||||||
this.emitter.on.apply @.emitter, arguments
|
@emitter.on.apply @emitter, arguments
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###* Fire an arbitrary event, scoped to "hmr:". ###
|
###* Fire an arbitrary event, scoped to "hmr:". ###
|
||||||
fire: (evtName, payload) ->
|
fire: (evtName, payload) ->
|
||||||
payload = payload || { }
|
payload = payload || { }
|
||||||
payload.cmd = this.moniker
|
payload.cmd = @moniker
|
||||||
this.emitter.emit 'hmr:' + evtName, payload
|
@emitter.emit 'hmr:' + evtName, payload
|
||||||
true
|
true
|
||||||
|
|
||||||
|
|
||||||
@ -60,10 +70,11 @@ Verb = module.exports = Class.extend
|
|||||||
stat: ( subEvent, payload ) ->
|
stat: ( subEvent, payload ) ->
|
||||||
payload = payload || { }
|
payload = payload || { }
|
||||||
payload.sub = subEvent
|
payload.sub = subEvent
|
||||||
this.fire 'status', payload
|
@fire 'status', payload
|
||||||
true
|
true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###* Associate error info with the invocation. ###
|
###* Associate error info with the invocation. ###
|
||||||
setError: ( code, obj ) ->
|
setError: ( code, obj ) ->
|
||||||
@errorCode = code
|
@errorCode = code
|
||||||
|
@ -9,3 +9,4 @@ require('./scripts/test-jrs-sheet');
|
|||||||
require('./scripts/test-themes');
|
require('./scripts/test-themes');
|
||||||
require('./scripts/test-api');
|
require('./scripts/test-api');
|
||||||
require('./scripts/test-output');
|
require('./scripts/test-output');
|
||||||
|
require('./scripts/test-dates');
|
||||||
|
81
test/scripts/test-dates.js
Normal file
81
test/scripts/test-dates.js
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
/**
|
||||||
|
@module test-dates.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
var chai = require('chai')
|
||||||
|
, expect = chai.expect
|
||||||
|
, should = chai.should()
|
||||||
|
, path = require('path')
|
||||||
|
, _ = require('underscore')
|
||||||
|
, FRESHResume = require('../../dist/core/fresh-resume')
|
||||||
|
, FCMD = require( '../../dist/index')
|
||||||
|
, validator = require('is-my-json-valid')
|
||||||
|
, EXTEND = require('extend');
|
||||||
|
|
||||||
|
chai.config.includeStack = true;
|
||||||
|
|
||||||
|
var gig = {
|
||||||
|
employer: 'E1'
|
||||||
|
};
|
||||||
|
|
||||||
|
var r = {
|
||||||
|
name: 'John Doe',
|
||||||
|
meta: {
|
||||||
|
format: 'FRESH@0.6.0'
|
||||||
|
},
|
||||||
|
employment: {
|
||||||
|
history: [ null ]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var tests = [
|
||||||
|
// single job, concrete start, no end
|
||||||
|
[ { start: '2010-01-01' } , { val: 6, unit: 'year' } ],
|
||||||
|
[ { start: '2010-01' } , { val: 6, unit: 'year' } ],
|
||||||
|
[ { start: '2010' } , { val: 6, unit: 'year' } ],
|
||||||
|
|
||||||
|
// single job, concrete start, concrete end
|
||||||
|
[ { start: '2010-01-01', end: '2015-01-01' } , { val: 5, unit: 'year' } ],
|
||||||
|
[ { start: '2010-01', end: '2015-01' } , { val: 5, unit: 'year' } ],
|
||||||
|
[ { start: '2010', end: '2015' } , { val: 5, unit: 'year' } ],
|
||||||
|
|
||||||
|
// single job, falsy start, falsy end
|
||||||
|
[ { } , { val: 0, unit: 'year' } ],
|
||||||
|
[ { start: null } , { val: 0, unit: 'year' } ],
|
||||||
|
[ { end: null } , { val: 0, unit: 'year' } ],
|
||||||
|
[ { start: undefined } , { val: 0, unit: 'year' } ],
|
||||||
|
[ { end: undefined } , { val: 0, unit: 'year' } ],
|
||||||
|
[ { start: null, end: null } , { val: 0, unit: 'year' } ],
|
||||||
|
[ { start: '', end: '' } , { val: 0, unit: 'year' } ],
|
||||||
|
[ { start: ' ', end: ' ' } , { val: 0, unit: 'year' } ],
|
||||||
|
[ { start: undefined, end: undefined } , { val: 0, unit: 'year' } ],
|
||||||
|
|
||||||
|
// two jobs (concrete start + end) -> ( concrete start )
|
||||||
|
[ { start: '2000-01', end: '2013-01' }, { start: '2013-01' }, { val: 16, unit: 'year' } ],
|
||||||
|
[ { start: '2000-01', end: '2013-01' }, { start: '2013-01', end: '' }, { val: 16, unit: 'year' } ],
|
||||||
|
[ { start: '2000-01', end: '2013-01' }, { start: '2013-01', end: null }, { val: 16, unit: 'year' } ],
|
||||||
|
[ { start: '2000-01', end: '2013-01' }, { start: '2013-01', end: 'current' }, { val: 16, unit: 'year' } ]
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
tests.forEach(function(t){
|
||||||
|
_.initial( t ).forEach(function(t){ t.employer = 'E1' });
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Testing DATES', function () {
|
||||||
|
|
||||||
|
tests.forEach( function(t) {
|
||||||
|
|
||||||
|
it( JSON.stringify( _.initial(t) ), function () {
|
||||||
|
r.employment.history = _.initial( t );
|
||||||
|
var rObj = new FRESHResume();
|
||||||
|
rObj.parseJSON( r );
|
||||||
|
var dur = rObj.duration();
|
||||||
|
expect( dur ).to.equal( _.last(t).val );
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
});
|
Reference in New Issue
Block a user