foldify

Import / require folder(s) of any type of files, and evaluate / curry the results.

foldify

Fold your folders, any way you like.

Import / require folders of any filetypes; evaluate / curry the results.

var foldify = require("foldify");
var routes = foldify(__dirname + "/lib/routes", {tree: true});
 
// routes.errors.500:  function(app){ app.get(...) } 
// routes.errors.501:  function(app){ app.get(...) } 
// routes.index:       function(app){ app.get(...) } 
// routes.user.login:  function(app){ app.get(...) } 
// routes.user.logout: function(app){ app.get(...) } 
 
routes(app, {whitelist: "errors/**"});
//just errors are now attached! 
 
routes(app);
//all routes are now attached! 

Clientside folding supported as well :-)

  • attach a folder of routes/controllers/etc easily (stop updating those pesky index.js files, who needs them??)
  • create a simple object hash of content, optionally filtered (for ex: grab all templates into a hash)
  • add a module w/ styles or templates (like bootstrap) to your project, then fold the style/etc folders you want from it. (helpful for including non-js dependencies in your project and keeping them clean.)
  • functional fun, curry as much as you like before / during evaluation
  • get creative!

npm install foldify

There are two steps to usage. First you must initialize the hash, by running foldify upon a directory name, an object, or an array of these. The provided object is useful in itself, but you can then evaluate the hash with provided arguments, or just curry them in. These steps take slightly different options, but both allow whitelisting/blacklisting of filenames/properties.

When the hash is initialized the result is a function object with properties whose content is taken from the respective files and names are taken from the filenames.

Note: .js and .json files are require()'d into the hash, while all other files are fs.readFileSync()'d.

Hash is something like:

{   filename: require("./filename.js"), 
    filename2: fs.readFileSync("./filename.html") }

dirname may be a path to a folder, node module, node module subdir path (ex: "foldify/test"), object, or array of any of these. In the case of an object, it is simply returned wrapped with the foldify functionality.

NOTE: in the browser, dirname may not be dynamic, except for __dirname which does get parsed. ex: foldify(__dirname + '/files') will work while foldify(path.join(__dirname, 'files')) will not.

var foldify = require("foldify");
 
var errorControllers = foldify(__dirname + "/lib/controllers/errors");

Even just this object can be useful:

//with a folder structure like: 
// ~/lib/controllers/errors/ 
//                      .../500.js 
//                      .../403.js 
//                      .../402.js 
 
app.get( '/500', errorControllers.500 );
app.get( '/403', errorControllers.403 );
app.get( '/402', errorControllers.402 );

also for...in compatible:

for(var controllerName in errorControllers){
    app.get( '/' + controllerName, errorControllers[controllerName] );
}

NOTE: due to browserify limitations, clientside initialization option properties may NOT be dynamically assigned, and must occur inline.

Include subfolders.

Include subfolders, and return hierarchical structure based on filepath.

output can be set to string, array, or object. If string or array, all files will be fs.readFileSync()'d.

Accepts string or array.
Uses minimatch upon filepaths using supplied whitelist patterns, supplied rules are prefixed with the curried directory. Reference minimatch documentation for matching behavior.

var foldify = require("foldify");
var stylesAndHtml = foldify(__dirname + "/client", {whitelist: ["*.less, *.html"], recursive: true});
//will grab all .less and .html files into hash, as strings 

Accepts string or array.
Uses minimatch upon filepaths using supplied blacklist patterns. Supplied rules are prefixed by the curried directory. Reference minimatch documentation for matching behavior.

var foldify = require("foldify");
var templates = foldify(__dirname + "/templates", {blacklist: ".json", recursive: true});
//will grab all files EXCEPT .json files 

When generating the property names for the hash, this determines whether to include extensions. Default is false for .js and .json, and true for all others. Manually setting this option will apply it to all filetypes.

When generating the property names for the hash, this determines whether to use the full filepath as the property name. This is defaulted to for cases of duplicate property names.

A benefit of fullPath is more flexibility with minimatch white/black listing at evaluation.

Import .js / .json files as strings rather than require them.

Change the encoding of files that are readFileSync'ed

Once the hash is initialized, it becomes a function that can be evaluated. It is also an object whose properties make up the hash. Everytime the function is evaluated, it returns another function-object that can also be evaluated.

Evaluation is very useful when keeping folders of like files that export functions taking similar arguments.

For example a folder of express routes a la:

module.exports = function(app){
    app.get('/', function(reqres){ res.end("hello world"); });
}

This represents the returned function object that is created after initialization.

args is optional, and may be a single argument, or an array of arguments (if you would like to pass in an actual array as an argument itself, you must encapsulate it with another array, or it will be parsed as if the values within that array represent arguments.)

Following from the example above, to attach all routes to the same express app:

var app = express();
var foldify = require("foldify");
 
var routes = foldify(__dirname + "/routes");
 
routes(app);
//all routes are attached! 

Accepts string or array.
Uses minimatch upon property names using supplied whitelist patterns. Reference minimatch documentation for matching behavior.

var foldify = require("foldify");
var routes = foldify(__dirname + "/routes");
routes(app, {whitelist: "a*"} );
//only connects routes beginning with "a" 

If trim option is set, returned function object will have non-whitelisted properties removed.

If hash is tree structure, whitelist rules should use / as a delimiter for properties. for ex: {whitelist: "path/to/property/**"}

Accepts string or array.
Uses minimatch upon property names using supplied blacklist patterns. Reference minimatch documentation for matching behavior.

var foldify = require("foldify");
var routes = foldify(__dirname + "/routes");
routes(app, {blacklist: "a*"} );
//connects routes except those beginning with "a" 

If trim option is set, returned function object will have blacklisted properties removed.

If hash is tree structure, blacklist rules should use / as a delimiter for properties. for ex: {blacklist: "path/to/property/**"}

Set to false if you only want to curry the hash's functions, not evaluate them.

With a folder 'mathFuncs' with files like:

module.exports = sum;
function sum(){
    var sum = [].slice.call(arguments, 0)
                .reduce(function(prevcurrent){ return (+prev || 0) + (+current || 0); });
    console.log("sum = " + sum);
}
var foldify = require("foldify");
var mathFuncs = foldify(__dirname + "/lib/mathFuncs");
 
//To curry only, set 'evaluate' to false: 
var curried = mathFuncs(1, {evaluate: false});
 
var curried2 = curried([2,3], {evaluate: false});
 
curried()
//sum = 1 
 
curried2()
//sum = 6 

If a function or property evaluates to undefined, or is blacklisted / outside of whitelist, then remove it.

Normally, when evaluated function returns undefined (as in the math example above), the function itself will be placed back into the hash but with the supplied arguments curried:

//reference evaluate option code 
 
var curried3 = curried2(10);
//sum = 16 
 
typeof curried3.mathFunc // function 
 
curried3()
//sum = 16 
 
var curried4 = curried3(20)
//sum = 36 
 
curried4()
//sum = 46 

But if allowUndefined is true, functions may return undefined into the hash instead of a curried version of itself:

var curried3 = curried2(10, {allowUndefined: true});
//sum = 16 
 
typeof curried3.mathFunc // undefined 
 
curried3()
//folding further will continue to produce `undefined` 

###completed:

  • server side and client side support (via supplied browserify transform)
  • can return a tree structure, keeps track where in the structure to evaluate without polluting object itself
  • can include npm modules or subfolders of npm modules (if you want to grab specific folder of css/less files from a module for example)
  • can be folded endlessly
  • whitelist / blacklist files or properties at each iteration (using minimatch)
  • compatible with for...in (no prototype properties to sort through)
  • tests in both server and browser
  • ie8 compatiblity thank the lawd
  • base64 encoding

###yet to be completed:

  • ability to wrap a function to transform individual results (needed?)
  • add tests for inputting arrays
  • add test for inputting and currying existing objects
  • add test for curry even upon evaluation (if undefined returned)
  • combine curry objects (a la extend)

Run npm install then npm test to test server code.

To test in browser please install browserify globally and testling globally, and then run testling -u in the root dir of this module.