1
0
mirror of https://github.com/JuanCanham/HackMyResume.git synced 2024-11-22 00:10:15 +00:00

Introduce formal generators.

Introduce a shallow hierarchy of simple generator classes, collecting
common functionality and allowing for future snap-in generator
replacement. Use John Resig's "class"-based inheritance per
http://ejohn.org/blog/simple-javascript-inheritance/.
This commit is contained in:
devlinjd 2015-09-23 00:03:49 -04:00
parent 7bf25469de
commit 7363e48019
8 changed files with 329 additions and 2 deletions

13
src/gen/base-generator.js Normal file
View File

@ -0,0 +1,13 @@
/**
Base resume generator for FluentCV.
@license Copyright (c) 2015 by James M. Devlin. All rights reserved.
*/
(function() {
var Class = require( './class' );
var BaseGenerator = module.exports = Class.extend({
init: function( outputFormat ) {
this.format = outputFormat;
}
});
}());

67
src/gen/class.js Normal file
View File

@ -0,0 +1,67 @@
/* Simple JavaScript Inheritance
* By John Resig http://ejohn.org/
* MIT Licensed.
* http://ejohn.org/blog/simple-javascript-inheritance/
*/
// Inspired by base2 and Prototype
(function(){
var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
// The base Class implementation (does nothing)
this.Class = function(){};
module.exports = Class;
// Create a new Class that inherits from this class
Class.extend = function(prop) {
var _super = this.prototype;
// Instantiate a base class (but only create the instance,
// don't run the init constructor)
initializing = true;
var prototype = new this();
initializing = false;
// Copy the properties over onto the new prototype
for (var name in prop) {
// Check if we're overwriting an existing function
prototype[name] = typeof prop[name] == "function" &&
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
(function(name, fn){
return function() {
var tmp = this._super;
// Add a new ._super() method that is the same method
// but on the super-class
this._super = _super[name];
// The method only need to be bound temporarily, so we
// remove it when we're done executing
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name]) :
prop[name];
}
// The dummy class constructor
function Class() {
// All construction is actually done in the init method
if ( !initializing && this.init )
this.init.apply(this, arguments);
}
// Populate our constructed prototype object
Class.prototype = prototype;
// Enforce the constructor to be what we expect
Class.prototype.constructor = Class;
// And make this class extendable
Class.extend = arguments.callee;
return Class;
};
})();

31
src/gen/html-generator.js Normal file
View File

@ -0,0 +1,31 @@
/**
HTML resume generator for FluentCV.
@license Copyright (c) 2015 by James M. Devlin. All rights reserved.
*/
var TemplateGenerator = require('./template-generator');
var FS = require('fs-extra');
var HTML = require( 'html' );
var HtmlGenerator = TemplateGenerator.extend({
init: function() {
this._super( 'html' );
},
/**
Generate an HTML resume with optional pretty printing.
*/
onBeforeSave: function( mk, themeFile, outputFile ) {
var cssSrc = themeFile.replace( /.html$/g, '.css' );
var cssDst = outputFile.replace( /.html$/g, '.css' );
FS.copy( cssSrc, cssDst, function( e ) {
if( e ) err( "Couldn't copy CSS file to destination: " + err);
});
return true ?
HTML.prettyPrint( mk, { indent_size: 2 } ) : mk;
}
});
module.exports = HtmlGenerator;

View File

@ -0,0 +1,31 @@
/**
HTML-based PDF resume generator for FluentCV.
@license Copyright (c) 2015 by James M. Devlin. All rights reserved.
*/
var TemplateGenerator = require('./template-generator');
var FS = require('fs-extra');
var HTML = require( 'html' );
var HtmlPdfGenerator = TemplateGenerator.extend({
init: function() {
this._super( 'pdf', 'html' );
},
/**
Generate an HTML resume with optional pretty printing.
*/
onBeforeSave: function( mk, themeFile, outputFile ) {
var cssSrc = themeFile.replace( /.html$/g, '.css' );
var cssDst = outputFile.replace( /.html$/g, '.css' );
FS.copy( cssSrc, cssDst, function( e ) {
if( e ) err( "Couldn't copy CSS file to destination: " + err);
});
return true ?
HTML.prettyPrint( mk, { indent_size: 2 } ) : mk;
}
});
module.exports = HtmlPdfGenerator;

View File

@ -0,0 +1,148 @@
/**
Template-based resume generator base for FluentCV.
@license Copyright (c) 2015 by James M. Devlin. All rights reserved.
*/
(function() {
var FS = require( 'fs' );
var BaseGenerator = require( './base-generator' );
var _ = require( 'underscore' );
var MD = require( 'marked' );
var XML = require( 'xml-escape' );
var path = require('path');
var _opts = {
keepBreaks: true,
nSym: '&newl;',
rSym: '&retn;',
template: {
interpolate: /\{\{(.+?)\}\}/g,
escape: /\{\{\=(.+?)\}\}/g,
evaluate: /\{\%(.+?)\%\}/g,
comment: /\{\#(.+?)\#\}/g
},
filters: {
out: function( txt ) { return txt; },
raw: function( txt ) { return txt; },
xml: function( txt ) { return XML(txt); },
md: function( txt ) { return MD(txt); },
mdin: function( txt ) { return MD(txt).replace(/^\s*\<p\>|\<\/p\>\s*$/gi, ''); },
lower: function( txt ) { return txt.toLowerCase(); }
},
prettyPrint: true,
prettyIndent: 2
};
var TemplateGenerator = BaseGenerator.extend({
/** outputFormat: html, txt, pdf, doc
templateFormat: html or txt
**/
init: function( outputFormat, templateFormat, cssFile ){
this._super( outputFormat );
this.tplFormat = templateFormat || outputFormat;
},
/** Default generation method for template-based generators. */
generate: function( rez, f, themeName ) {
try {
// Get the output file type (pdf, html, txt, etc)
var fName = path.basename( f, '.' + this.format );
// Load the active theme file, including CSS data if req'd
var themeFile = path.join( __dirname, '../../../watermark/', themeName, this.format + '.' + this.tplFormat );
var cssData = this.tplFormat === 'html' ? FS.readFileSync( path.join( __dirname, '../../../watermark/', themeName, 'html.css' ), 'utf8' ) : null;
var mk = FS.readFileSync( themeFile, 'utf8' );
// Compile and invoke the template!
mk = this.single( rez, mk, this.format, cssData, fName );
this.onBeforeSave && (mk = this.onBeforeSave( mk, themeFile, f ));
// Post-process and save the file
FS.writeFileSync( f, mk, 'utf8' );
return mk;
}
catch( ex ) {
err( ex );
}
},
/**
Perform a single resume JSON-to-DEST resume transformation. Exists as a
separate function in order to expose string-based transformations to clients
who don't have access to filesystem resources (in-browser, etc.).
*/
single: function( json, jst, format, styles, fName ) {
// Freeze whitespace in the template
_opts.keepBreaks && ( jst = freeze(jst) );
// Tweak underscore's default template delimeters
_.templateSettings = _opts.template;
// Convert {{ someVar }} to {% print(filt.out(someVar) %}
// Convert {{ someVar|someFilter }} to {% print(filt.someFilter(someVar) %}
jst = jst.replace( _.templateSettings.interpolate, function replace(m, p1) {
if( p1.indexOf('|') > -1 ) {
var terms = p1.split('|');
return '{% print( filt.' + terms[1] + '( ' + terms[0] + ' )) %}';
}
else {
return '{% print( filt.out(' + p1 + ') ) %}';
}
});
// Strip {# comments #}
jst = jst.replace( _.templateSettings.comment, '');
json.display_progress_bar = true;
// Compile and run the template. TODO: avoid unnecessary recompiles.
jst = _.template( jst )({ r: json, css: styles, embedCss: false, cssFile: fName, filt: _opts.filters });
// Unfreeze whitespace
_opts.keepBreaks && ( jst = unfreeze(jst) );
return jst;
}
});
/**
Export the TemplateGenerator function/ctor.
*/
module.exports = TemplateGenerator;
/**
Freeze newlines for protection against errant JST parsers.
*/
function freeze( markup ) {
return markup
.replace( _reg.regN, _opts.nSym )
.replace( _reg.regR, _opts.rSym );
}
/**
Unfreeze newlines when the coast is clear.
*/
function unfreeze( markup ) {
return markup
.replace( _reg.regSymR, '\r' )
.replace( _reg.regSymN, '\n' );
}
/**
Regexes for linebreak preservation.
*/
var _reg = {
regN: new RegExp( '\n', 'g' ),
regR: new RegExp( '\r', 'g' ),
regSymN: new RegExp( _opts.nSym, 'g' ),
regSymR: new RegExp( _opts.rSym, 'g' )
};
}());

15
src/gen/text-generator.js Normal file
View File

@ -0,0 +1,15 @@
/**
Plain text resume generator for FluentCV.
@license Copyright (c) 2015 by James M. Devlin. All rights reserved.
*/
var TemplateGenerator = require('./template-generator');
var TextGenerator = TemplateGenerator.extend({
init: function(){
this._super( 'txt' );
},
});
module.exports = TextGenerator;

13
src/gen/word-generator.js Normal file
View File

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

View File

@ -13,7 +13,11 @@ module.exports = function () {
, path = require( 'path' ) , path = require( 'path' )
, extend = require( './extend' ) , extend = require( './extend' )
, _ = require('underscore') , _ = require('underscore')
, Sheet = require('./sheet'); , Sheet = require('./sheet')
, HtmlGenerator = require('./gen/html-generator')
, TextGenerator = require('./gen/text-generator')
, HtmlPdfGenerator = require('./gen/html-pdf-generator')
, WordGenerator = require('./gen/word-generator');
String.prototype.endsWith = function(suffix) { String.prototype.endsWith = function(suffix) {
return this.indexOf(suffix, this.length - suffix.length) !== -1; return this.indexOf(suffix, this.length - suffix.length) !== -1;
@ -281,7 +285,12 @@ module.exports = function () {
generate: hmr, generate: hmr,
transform: single, transform: single,
options: _opts, options: _opts,
formats: _fmts formats: _fmts,
Sheet: Sheet,
HtmlGenerator: HtmlGenerator,
TextGenerator: TextGenerator,
HtmlPdfGenerator: HtmlPdfGenerator,
WordGenerator: WordGenerator
}; };
}(); }();