├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── lib ├── args.js ├── base64.js ├── file.js ├── format.js └── index.js ├── package.json └── test ├── file-test.js ├── fixtures ├── read-json-file │ └── config.json └── require-directory │ ├── directory │ └── index.js │ └── helloWorld.js ├── format-test.js ├── function-args-test.js ├── helpers └── macros.js ├── random-string-test.js ├── require-directory-test.js └── utile-test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | *.swp 4 | *.swo 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.8 4 | - 0.10 5 | 6 | notifications: 7 | email: 8 | - travis@nodejitsu.com 9 | irc: "irc.freenode.org#nodejitsu" 10 | 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | 0.1.5 / 2012-09-18 3 | ================== 4 | 5 | * Fixed problem with underscore values in camelToUnderscore 6 | 7 | 0.1.4 / 2012-07-26 8 | ================== 9 | 10 | * Made use of inflect for camel to underscore conversion 11 | 12 | 0.1.3 / 2012-07-25 13 | ================== 14 | 15 | * Added camel to underscore conversion and vice-versa 16 | 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, Charlie Robbins & the Contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # utile [![Build Status](https://secure.travis-ci.org/flatiron/utile.png)](http://travis-ci.org/flatiron/utile) 2 | 3 | A drop-in replacement for `util` with some additional advantageous functions 4 | 5 | ## Motivation 6 | Javascript is definitely a "batteries not included language" when compared to languages like Ruby or Python. Node.js has a simple utility library which exposes some basic (but important) functionality: 7 | 8 | ``` 9 | $ node 10 | > var util = require('util'); 11 | > util. 12 | (...) 13 | 14 | util.debug util.error util.exec util.inherits util.inspect 15 | util.log util.p util.print util.pump util.puts 16 | ``` 17 | 18 | When one considers their own utility library, why ever bother requiring `util` again? That is the approach taken by this module. To compare: 19 | 20 | ``` 21 | $ node 22 | > var utile = require('./lib') 23 | > utile. 24 | (...) 25 | 26 | utile.async utile.capitalize utile.clone utile.cpr utile.createPath utile.debug 27 | utile.each utile.error utile.exec utile.file utile.filter utile.find 28 | utile.inherits utile.log utile.mixin utile.mkdirp utile.p utile.path 29 | utile.print utile.pump utile.puts utile.randomString utile.requireDir uile.requireDirLazy 30 | utile.rimraf 31 | ``` 32 | 33 | As you can see all of the original methods from `util` are there, but there are several new methods specific to `utile`. A note about implementation: _no node.js native modules are modified by utile, it simply copies those methods._ 34 | 35 | ## Methods 36 | The `utile` modules exposes some simple utility methods: 37 | 38 | * `.each(obj, iterator)`: Iterate over the keys of an object. 39 | * `.mixin(target [source0, source1, ...])`: Copies enumerable properties from `source0 ... sourceN` onto `target` and returns the resulting object. 40 | * `.clone(obj)`: Shallow clones the specified object. 41 | * `.capitalize(str)`: Capitalizes the specified `str`. 42 | * `.randomString(length)`: randomString returns a pseudo-random ASCII string (subset) the return value is a string of length ⌈bits/6⌉ of characters from the base64 alphabet. 43 | * `.filter(obj, test)`: return an object with the properties that `test` returns true on. 44 | * `.args(arguments)`: Converts function arguments into actual array with special `callback`, `cb`, `array`, and `last` properties. Also supports *optional* argument contracts. See [the example](https://github.com/flatiron/utile/blob/master/examples/utile-args.js) for more details. 45 | * `.requireDir(directory)`: Requires all files and directories from `directory`, returning an object with keys being filenames (without trailing `.js`) and respective values being return values of `require(filename)`. 46 | * `.requireDirLazy(directory)`: Lazily requires all files and directories from `directory`, returning an object with keys being filenames (without trailing `.js`) and respective values (getters) being return values of `require(filename)`. 47 | * `.format([string] text, [array] formats, [array] replacements)`: Replace `formats` in `text` with `replacements`. This will fall back to the original `util.format` command if it is called improperly. 48 | 49 | ## Packaged Dependencies 50 | In addition to the methods that are built-in, utile includes a number of commonly used dependencies to reduce the number of includes in your package.json. These modules _are not eagerly loaded to be respectful of startup time,_ but instead are lazy-loaded getters on the `utile` object 51 | 52 | * `.async`: [Async utilities for node and the browser][0] 53 | * `.inflect`: [Customizable inflections for node.js][6] 54 | * `.mkdirp`: [Recursively mkdir, like mkdir -p, but in node.js][1] 55 | * `.rimraf`: [A rm -rf util for nodejs][2] 56 | * `.cpr`: [Asynchronous recursive file copying with Node.js][3] 57 | 58 | ## Installation 59 | 60 | ### Installing npm (node package manager) 61 | ``` 62 | curl http://npmjs.org/install.sh | sh 63 | ``` 64 | 65 | ### Installing utile 66 | ``` 67 | [sudo] npm install utile 68 | ``` 69 | 70 | ## Tests 71 | All tests are written with [vows][4] and should be run with [npm][5]: 72 | 73 | ``` bash 74 | $ npm test 75 | ``` 76 | 77 | #### Author: [Charlie Robbins](http://github.com/indexzero) 78 | #### Contributors: [Dominic Tarr](http://github.com/dominictarr), [Marak Squires](https://github.com/marak) 79 | #### License: MIT 80 | 81 | [0]: https://github.com/caolan/async 82 | [1]: https://github.com/substack/node-mkdirp 83 | [2]: https://github.com/isaacs/rimraf 84 | [3]: https://github.com/avianflu/ncp 85 | [4]: https://vowsjs.org 86 | [5]: https://npmjs.org 87 | [6]: https://github.com/pksunkara/inflect 88 | -------------------------------------------------------------------------------- /lib/args.js: -------------------------------------------------------------------------------- 1 | /* 2 | * args.js: function argument parsing helper utility 3 | * 4 | * (C) 2011, Charlie Robbins & the Contributors 5 | * MIT LICENSE 6 | * 7 | */ 8 | 9 | var utile = require('./index'); 10 | 11 | // 12 | // ### function args(_args) 13 | // #### _args {Arguments} Original function arguments 14 | // 15 | // Top-level method will accept a javascript "arguments" object (the actual keyword 16 | // "arguments" inside any scope), and attempt to return back an intelligent object 17 | // representing the functions arguments 18 | // 19 | module.exports = function (_args) { 20 | var args = utile.rargs(_args), 21 | _cb; 22 | 23 | // 24 | // Find and define the first argument 25 | // 26 | Object.defineProperty(args, 'first', { value: args[0] }); 27 | 28 | // 29 | // Find and define any callback 30 | // 31 | _cb = args[args.length - 1] || args[args.length]; 32 | if (typeof _cb === "function") { 33 | Object.defineProperty(args, 'callback', { value: _cb }); 34 | Object.defineProperty(args, 'cb', { value: _cb }); 35 | args.pop(); 36 | } 37 | 38 | // 39 | // Find and define the last argument 40 | // 41 | if (args.length) { 42 | Object.defineProperty(args, 'last', { value: args[args.length - 1] }); 43 | } 44 | 45 | return args; 46 | }; 47 | -------------------------------------------------------------------------------- /lib/base64.js: -------------------------------------------------------------------------------- 1 | /* 2 | * base64.js: An extremely simple implementation of base64 encoding / decoding using node.js Buffers 3 | * 4 | * (C) 2010, Charlie Robbins & the Contributors. 5 | * 6 | */ 7 | 8 | var base64 = exports; 9 | 10 | // 11 | // ### function encode (unencoded) 12 | // #### @unencoded {string} The string to base64 encode 13 | // Encodes the specified string to base64 using node.js Buffers. 14 | // 15 | base64.encode = function (unencoded) { 16 | var encoded; 17 | 18 | try { 19 | encoded = new Buffer(unencoded || '').toString('base64'); 20 | } 21 | catch (ex) { 22 | return null; 23 | } 24 | 25 | return encoded; 26 | }; 27 | 28 | // 29 | // ### function decode (encoded) 30 | // #### @encoded {string} The string to base64 decode 31 | // Decodes the specified string from base64 using node.js Buffers. 32 | // 33 | base64.decode = function (encoded) { 34 | var decoded; 35 | 36 | try { 37 | decoded = new Buffer(encoded || '', 'base64').toString('utf8'); 38 | } 39 | catch (ex) { 40 | return null; 41 | } 42 | 43 | return decoded; 44 | }; 45 | -------------------------------------------------------------------------------- /lib/file.js: -------------------------------------------------------------------------------- 1 | /* 2 | * file.js: Simple utilities for working with the file system. 3 | * 4 | * (C) 2011, Charlie Robbins & the Contributors 5 | * MIT LICENSE 6 | * 7 | */ 8 | 9 | var fs = require('fs'); 10 | 11 | exports.readJson = exports.readJSON = function (file, callback) { 12 | if (typeof callback !== 'function') { 13 | throw new Error('utile.file.readJson needs a callback'); 14 | } 15 | 16 | fs.readFile(file, 'utf-8', function (err, data) { 17 | if (err) { 18 | return callback(err); 19 | } 20 | 21 | try { 22 | var json = JSON.parse(data); 23 | callback(null, json); 24 | } 25 | catch (err) { 26 | return callback(err); 27 | } 28 | }); 29 | }; 30 | 31 | exports.readJsonSync = exports.readJSONSync = function (file) { 32 | return JSON.parse(fs.readFileSync(file, 'utf-8')); 33 | }; 34 | -------------------------------------------------------------------------------- /lib/format.js: -------------------------------------------------------------------------------- 1 | /* 2 | * format.js: `util.format` enhancement to allow custom formatting parameters. 3 | * 4 | * (C) 2011, Charlie Robbins & the Contributors 5 | * MIT LICENSE 6 | * 7 | */ 8 | 9 | var util = require('util'); 10 | 11 | exports = module.exports = function(str) { 12 | var formats = [].slice.call(arguments, 1, 3); 13 | 14 | if (!(formats[0] instanceof Array && formats[1] instanceof Array) || arguments.length > 3) 15 | return util.format.apply(null, arguments); 16 | 17 | var replacements = formats.pop(), 18 | formats = formats.shift(); 19 | 20 | formats.forEach(function(format, id) { 21 | str = str.replace(new RegExp(format), replacements[id]); 22 | }); 23 | 24 | return str; 25 | }; 26 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * index.js: Top-level include for the `utile` module. 3 | * 4 | * (C) 2011, Charlie Robbins & the Contributors 5 | * MIT LICENSE 6 | * 7 | */ 8 | 9 | var fs = require('fs'), 10 | path = require('path'), 11 | util = require('util'); 12 | 13 | var utile = module.exports; 14 | 15 | // 16 | // Extend the `utile` object with all methods from the 17 | // core node `util` methods. 18 | // 19 | Object.keys(util).forEach(function (key) { 20 | utile[key] = util[key]; 21 | }); 22 | 23 | Object.defineProperties(utile, { 24 | 25 | // 26 | // ### function async 27 | // Simple wrapper to `require('async')`. 28 | // 29 | 'async': { 30 | get: function() { 31 | return utile.async = require('async'); 32 | } 33 | }, 34 | 35 | // 36 | // ### function inflect 37 | // Simple wrapper to `require('i')`. 38 | // 39 | 'inflect': { 40 | get: function() { 41 | return utile.inflect = require('i')(); 42 | } 43 | }, 44 | 45 | // 46 | // ### function mkdirp 47 | // Simple wrapper to `require('mkdirp')` 48 | // 49 | 'mkdirp': { 50 | get: function() { 51 | return utile.mkdirp = require('mkdirp'); 52 | } 53 | }, 54 | 55 | // 56 | // ### function deepEqual 57 | // Simple wrapper to `require('deep-equal')` 58 | // Remark: deepEqual is 4x faster then using assert.deepEqual 59 | // see: https://gist.github.com/2790507 60 | // 61 | 'deepEqual': { 62 | get: function() { 63 | return utile.deepEqual = require('deep-equal'); 64 | } 65 | }, 66 | 67 | // 68 | // ### function rimraf 69 | // Simple wrapper to `require('rimraf')` 70 | // 71 | 'rimraf': { 72 | get: function() { 73 | return utile.rimraf = require('rimraf'); 74 | } 75 | }, 76 | 77 | // 78 | // ### function cpr 79 | // Simple wrapper to `require('ncp').ncp` 80 | // 81 | 'cpr': { 82 | get: function() { 83 | return utile.cpr = require('ncp').ncp; 84 | } 85 | }, 86 | 87 | // 88 | // ### @file {Object} 89 | // Lazy-loaded `file` module 90 | // 91 | 'file': { 92 | get: function() { 93 | return utile.file = require('./file'); 94 | } 95 | }, 96 | 97 | // 98 | // ### @args {Object} 99 | // Lazy-loaded `args` module 100 | // 101 | 'args': { 102 | get: function() { 103 | return utile.args = require('./args'); 104 | } 105 | }, 106 | 107 | // 108 | // ### @base64 {Object} 109 | // Lazy-loaded `base64` object 110 | // 111 | 'base64': { 112 | get: function() { 113 | return utile.base64 = require('./base64'); 114 | } 115 | }, 116 | 117 | // 118 | // ### @format {Object} 119 | // Lazy-loaded `format` object 120 | // 121 | 'format': { 122 | get: function() { 123 | return utile.format = require('./format'); 124 | } 125 | } 126 | 127 | }); 128 | 129 | 130 | // 131 | // ### function rargs(_args) 132 | // #### _args {Arguments} Original function arguments 133 | // 134 | // Top-level method will accept a javascript "arguments" object 135 | // (the actual keyword "arguments" inside any scope) and return 136 | // back an Array. 137 | // 138 | utile.rargs = function (_args, slice) { 139 | if (!slice) { 140 | slice = 0; 141 | } 142 | 143 | var len = (_args || []).length, 144 | args = new Array(len - slice), 145 | i; 146 | 147 | // 148 | // Convert the raw `_args` to a proper Array. 149 | // 150 | for (i = slice; i < len; i++) { 151 | args[i - slice] = _args[i]; 152 | } 153 | 154 | return args; 155 | }; 156 | 157 | // 158 | // ### function each (obj, iterator) 159 | // #### @obj {Object} Object to iterate over 160 | // #### @iterator {function} Continuation to use on each key. `function (value, key, object)` 161 | // Iterate over the keys of an object. 162 | // 163 | utile.each = function (obj, iterator) { 164 | Object.keys(obj).forEach(function (key) { 165 | iterator(obj[key], key, obj); 166 | }); 167 | }; 168 | 169 | // 170 | // ### function find (o) 171 | // 172 | // 173 | utile.find = function (obj, pred) { 174 | var value, key; 175 | 176 | for (key in obj) { 177 | value = obj[key]; 178 | if (pred(value, key)) { 179 | return value; 180 | } 181 | } 182 | }; 183 | 184 | // 185 | // ### function pad (str, len, chr) 186 | // ### @str {String} String to pad 187 | // ### @len {Number} Number of chars to pad str with 188 | // ### @chr {String} Optional replacement character, defaults to empty space 189 | // Appends chr to str until it reaches a length of len 190 | // 191 | utile.pad = function pad(str, len, chr) { 192 | var s; 193 | if (!chr) { 194 | chr = ' '; 195 | } 196 | str = str || ''; 197 | s = str; 198 | if (str.length < len) { 199 | for (var i = 0; i < (len - str.length); i++) { 200 | s += chr; 201 | } 202 | } 203 | return s; 204 | } 205 | 206 | // 207 | // ### function path (obj, path, value) 208 | // ### @obj {Object} Object to insert value into 209 | // ### @path {Array} List of nested keys to insert value at 210 | // Retreives a value from given Object, `obj`, located at the 211 | // nested keys, `path`. 212 | // 213 | utile.path = function (obj, path) { 214 | var key, i; 215 | 216 | for (i in path) { 217 | if (typeof obj === 'undefined') { 218 | return undefined; 219 | } 220 | 221 | key = path[i]; 222 | obj = obj[key]; 223 | } 224 | 225 | return obj; 226 | }; 227 | 228 | // 229 | // ### function createPath (obj, path, value) 230 | // ### @obj {Object} Object to insert value into 231 | // ### @path {Array} List of nested keys to insert value at 232 | // ### @value {*} Value to insert into the object. 233 | // Inserts the `value` into the given Object, `obj`, creating 234 | // any keys in `path` along the way if necessary. 235 | // 236 | utile.createPath = function (obj, path, value) { 237 | var key, i; 238 | 239 | for (i in path) { 240 | key = path[i]; 241 | if (!obj[key]) { 242 | obj[key] = ((+i + 1 === path.length) ? value : {}); 243 | } 244 | 245 | obj = obj[key]; 246 | } 247 | }; 248 | 249 | // 250 | // ### function mixin (target [source0, source1, ...]) 251 | // Copies enumerable properties from `source0 ... sourceN` 252 | // onto `target` and returns the resulting object. 253 | // 254 | utile.mixin = function (target) { 255 | utile.rargs(arguments, 1).forEach(function (o) { 256 | Object.getOwnPropertyNames(o).forEach(function(attr) { 257 | var getter = Object.getOwnPropertyDescriptor(o, attr).get, 258 | setter = Object.getOwnPropertyDescriptor(o, attr).set; 259 | 260 | if (!getter && !setter) { 261 | target[attr] = o[attr]; 262 | } 263 | else { 264 | Object.defineProperty(target, attr, { 265 | get: getter, 266 | set: setter 267 | }); 268 | } 269 | }); 270 | }); 271 | 272 | return target; 273 | }; 274 | 275 | 276 | // 277 | // ### function capitalize (str) 278 | // #### @str {string} String to capitalize 279 | // Capitalizes the specified `str`. 280 | // 281 | utile.capitalize = utile.inflect.camelize; 282 | 283 | // 284 | // ### function escapeRegExp (str) 285 | // #### @str {string} String to be escaped 286 | // Escape string for use in Javascript regex 287 | // 288 | utile.escapeRegExp = function (str) { 289 | return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); 290 | }; 291 | 292 | // 293 | // ### function randomString (length) 294 | // #### @length {integer} The number of bits for the random base64 string returned to contain 295 | // randomString returns a pseude-random ASCII string (subset) 296 | // the return value is a string of length ⌈bits/6⌉ of characters 297 | // from the base64 alphabet. 298 | // 299 | utile.randomString = function (length) { 300 | var chars, rand, i, ret, mod, bits; 301 | 302 | chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-'; 303 | ret = ''; 304 | // standard 4 305 | mod = 4; 306 | // default is 16 307 | bits = length * mod || 64; 308 | 309 | // in v8, Math.random() yields 32 pseudo-random bits (in spidermonkey it gives 53) 310 | while (bits > 0) { 311 | // 32-bit integer 312 | rand = Math.floor(Math.random() * 0x100000000); 313 | //we use the top bits 314 | for (i = 26; i > 0 && bits > 0; i -= mod, bits -= mod) { 315 | ret += chars[0x3F & rand >>> i]; 316 | } 317 | } 318 | 319 | return ret; 320 | }; 321 | 322 | // 323 | // ### function filter (object, test) 324 | // #### @obj {Object} Object to iterate over 325 | // #### @pred {function} Predicate applied to each property. `function (value, key, object)` 326 | // Returns an object with properties from `obj` which satisfy 327 | // the predicate `pred` 328 | // 329 | utile.filter = function (obj, pred) { 330 | var copy; 331 | if (Array.isArray(obj)) { 332 | copy = []; 333 | utile.each(obj, function (val, key) { 334 | if (pred(val, key, obj)) { 335 | copy.push(val); 336 | } 337 | }); 338 | } 339 | else { 340 | copy = {}; 341 | utile.each(obj, function (val, key) { 342 | if (pred(val, key, obj)) { 343 | copy[key] = val; 344 | } 345 | }); 346 | } 347 | return copy; 348 | }; 349 | 350 | // 351 | // ### function requireDir (directory) 352 | // #### @directory {string} Directory to require 353 | // Requires all files and directories from `directory`, returning an object 354 | // with keys being filenames (without trailing `.js`) and respective values 355 | // being return values of `require(filename)`. 356 | // 357 | utile.requireDir = function (directory) { 358 | var result = {}, 359 | files = fs.readdirSync(directory); 360 | 361 | files.forEach(function (file) { 362 | if (file.substr(-3) === '.js') { 363 | file = file.substr(0, file.length - 3); 364 | } 365 | result[file] = require(path.resolve(directory, file)); 366 | }); 367 | return result; 368 | }; 369 | 370 | // 371 | // ### function requireDirLazy (directory) 372 | // #### @directory {string} Directory to require 373 | // Lazily requires all files and directories from `directory`, returning an 374 | // object with keys being filenames (without trailing `.js`) and respective 375 | // values (getters) being return values of `require(filename)`. 376 | // 377 | utile.requireDirLazy = function (directory) { 378 | var result = {}, 379 | files = fs.readdirSync(directory); 380 | 381 | files.forEach(function (file) { 382 | if (file.substr(-3) === '.js') { 383 | file = file.substr(0, file.length - 3); 384 | } 385 | Object.defineProperty(result, file, { 386 | get: function() { 387 | return result[file] = require(path.resolve(directory, file)); 388 | } 389 | }); 390 | }); 391 | 392 | return result; 393 | }; 394 | 395 | // 396 | // ### function clone (object, filter) 397 | // #### @object {Object} Object to clone 398 | // #### @filter {Function} Filter to be used 399 | // Shallow clones the specified object. 400 | // 401 | utile.clone = function (object, filter) { 402 | return Object.keys(object).reduce(filter ? function (obj, k) { 403 | if (filter(k)) obj[k] = object[k]; 404 | return obj; 405 | } : function (obj, k) { 406 | obj[k] = object[k]; 407 | return obj; 408 | }, {}); 409 | }; 410 | 411 | // 412 | // ### function camelToUnderscore (obj) 413 | // #### @obj {Object} Object to convert keys on. 414 | // Converts all keys of the type `keyName` to `key_name` on the 415 | // specified `obj`. 416 | // 417 | utile.camelToUnderscore = function (obj) { 418 | if (typeof obj !== 'object' || obj === null) { 419 | return obj; 420 | } 421 | 422 | if (Array.isArray(obj)) { 423 | obj.forEach(utile.camelToUnderscore); 424 | return obj; 425 | } 426 | 427 | Object.keys(obj).forEach(function (key) { 428 | var k = utile.inflect.underscore(key); 429 | if (k !== key) { 430 | obj[k] = obj[key]; 431 | delete obj[key]; 432 | key = k; 433 | } 434 | utile.camelToUnderscore(obj[key]); 435 | }); 436 | 437 | return obj; 438 | }; 439 | 440 | // 441 | // ### function underscoreToCamel (obj) 442 | // #### @obj {Object} Object to convert keys on. 443 | // Converts all keys of the type `key_name` to `keyName` on the 444 | // specified `obj`. 445 | // 446 | utile.underscoreToCamel = function (obj) { 447 | if (typeof obj !== 'object' || obj === null) { 448 | return obj; 449 | } 450 | 451 | if (Array.isArray(obj)) { 452 | obj.forEach(utile.underscoreToCamel); 453 | return obj; 454 | } 455 | 456 | Object.keys(obj).forEach(function (key) { 457 | var k = utile.inflect.camelize(key, false); 458 | if (k !== key) { 459 | obj[k] = obj[key]; 460 | delete obj[key]; 461 | key = k; 462 | } 463 | utile.underscoreToCamel(obj[key]); 464 | }); 465 | 466 | return obj; 467 | }; 468 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "utile", 3 | "description": "A drop-in replacement for `util` with some additional advantageous functions", 4 | "version": "0.3.0", 5 | "author": "Nodejitsu Inc. ", 6 | "maintainers": [ 7 | "indexzero " 8 | ], 9 | "repository": { 10 | "type": "git", 11 | "url": "http://github.com/flatiron/utile.git" 12 | }, 13 | "dependencies": { 14 | "async": "~0.9.0", 15 | "deep-equal": "~0.2.1", 16 | "i": "0.3.x", 17 | "mkdirp": "0.x.x", 18 | "ncp": "1.0.x", 19 | "rimraf": "2.x.x" 20 | }, 21 | "devDependencies": { 22 | "vows": "0.8.x" 23 | }, 24 | "scripts": { 25 | "test": "vows --spec" 26 | }, 27 | "main": "./lib/index", 28 | "engines": { 29 | "node": ">= 0.8.0" 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /test/file-test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * file-test.js: Tests for `utile.file` module. 3 | * 4 | * (C) 2011, Charlie Robbins & the Contributors 5 | * MIT LICENSE 6 | * 7 | */ 8 | 9 | var assert = require('assert'), 10 | path = require('path'), 11 | vows = require('vows'), 12 | macros = require('./helpers/macros'), 13 | utile = require('../'); 14 | 15 | var fixture = path.join(__dirname, 'fixtures', 'read-json-file', 'config.json'); 16 | 17 | vows.describe('utile/file').addBatch({ 18 | 'When using utile': { 19 | 'the `.file.readJson()` function': { 20 | topic: function () { 21 | utile.file.readJson(fixture, this.callback); 22 | }, 23 | 'should return correct JSON structure': macros.assertReadCorrectJson 24 | }, 25 | 'the `.file.readJsonSync()` function': { 26 | topic: utile.file.readJsonSync(fixture), 27 | 'should return correct JSON structure': macros.assertReadCorrectJson 28 | } 29 | } 30 | }).export(module); 31 | 32 | -------------------------------------------------------------------------------- /test/fixtures/read-json-file/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "World", 3 | "I am": ["the utile module"], 4 | "thisMakesMe": { 5 | "really": 1337, 6 | "right?": true 7 | } 8 | } 9 | 10 | -------------------------------------------------------------------------------- /test/fixtures/require-directory/directory/index.js: -------------------------------------------------------------------------------- 1 | exports.me = 'directory/index.js'; 2 | 3 | -------------------------------------------------------------------------------- /test/fixtures/require-directory/helloWorld.js: -------------------------------------------------------------------------------- 1 | exports.me = 'helloWorld.js'; 2 | 3 | -------------------------------------------------------------------------------- /test/format-test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * format-test.js: Tests for `utile.format` module. 3 | * 4 | * (C) 2011, Charlie Robbins & the Contributors 5 | * MIT LICENSE 6 | * 7 | */ 8 | 9 | var vows = require('vows'), 10 | assert = require('assert'), 11 | utile = require('../lib'); 12 | 13 | vows.describe('utile/format').addBatch({ 14 | 15 | 'Should use the original `util.format` if there are no custom parameters to replace.': function() { 16 | assert.equal(utile.format('%s %s %s', 'test', 'test2', 'test3'), 'test test2 test3'); 17 | }, 18 | 19 | 'Should use `utile.format` if custom parameters are provided.': function() { 20 | assert.equal(utile.format('%a %b %c', [ 21 | '%a', 22 | '%b', 23 | '%c' 24 | ], [ 25 | 'test', 26 | 'test2', 27 | 'test3' 28 | ]), 'test test2 test3'); 29 | } 30 | 31 | }).export(module); 32 | -------------------------------------------------------------------------------- /test/function-args-test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * function-args-test.js: Tests for `args` method 3 | * 4 | * (C) 2011, Charlie Robbins & the Contributors 5 | * MIT LICENSE 6 | * 7 | */ 8 | 9 | var assert = require('assert'), 10 | path = require('path'), 11 | vows = require('vows'), 12 | macros = require('./helpers/macros'), 13 | utile = require('../'); 14 | 15 | vows.describe('utile/args').addBatch({ 16 | 'When using utile': { 17 | 'the `args` function': { 18 | topic: utile, 19 | 'should be a function': function (_utile) { 20 | assert.isFunction(_utile.args); 21 | }, 22 | } 23 | }, 24 | 'utile.rargs()': { 25 | 'with no arguments': { 26 | topic: utile.rargs(), 27 | 'should return an empty object': function (result) { 28 | assert.isArray(result); 29 | assert.lengthOf(result, 0); 30 | } 31 | }, 32 | 'with simple arguments': { 33 | topic: function () { 34 | return (function () { 35 | return utile.rargs(arguments); 36 | })('a', 'b', 'c'); 37 | }, 38 | 'should return an array with three items': function (result) { 39 | assert.isArray(result); 40 | assert.equal(3, result.length); 41 | assert.equal(result[0], 'a'); 42 | assert.equal(result[1], 'b'); 43 | assert.equal(result[2], 'c'); 44 | } 45 | }, 46 | 'with a simple slice': { 47 | topic: function () { 48 | return (function () { 49 | return utile.rargs(arguments, 1); 50 | })('a', 'b', 'c'); 51 | }, 52 | 'should return an array with three items': function (result) { 53 | assert.isArray(result); 54 | assert.equal(2, result.length); 55 | assert.equal(result[0], 'b'); 56 | assert.equal(result[1], 'c'); 57 | } 58 | } 59 | }, 60 | 'utile.args()': { 61 | 'with no arguments': { 62 | topic: utile.args(), 63 | 'should return an empty Array': function (result) { 64 | assert.isUndefined(result.callback); 65 | assert.isArray(result); 66 | assert.lengthOf(result, 0); 67 | } 68 | }, 69 | 'with simple arguments': { 70 | topic: function () { 71 | return (function () { 72 | return utile.args(arguments); 73 | })('a', 'b', 'c', function () { 74 | return 'ok'; 75 | }); 76 | }, 77 | 'should return an array with three items': function (result) { 78 | assert.isArray(result); 79 | assert.equal(3, result.length); 80 | assert.equal(result[0], 'a'); 81 | assert.equal(result[1], 'b'); 82 | assert.equal(result[2], 'c'); 83 | 84 | // 85 | // Ensure that the Array returned 86 | // by `utile.args()` enumerates correctly 87 | // 88 | var length = 0; 89 | result.forEach(function (item) { 90 | length++; 91 | }); 92 | 93 | assert.equal(length, 3); 94 | }, 95 | 'should return lookup helpers': function (result) { 96 | assert.isArray(result); 97 | assert.equal(result.first, 'a'); 98 | assert.equal(result.last, 'c'); 99 | assert.isFunction(result.callback); 100 | assert.isFunction(result.cb); 101 | } 102 | } 103 | } 104 | }).export(module); 105 | -------------------------------------------------------------------------------- /test/helpers/macros.js: -------------------------------------------------------------------------------- 1 | /* 2 | * macros.js: Test macros for `utile` module. 3 | * 4 | * (C) 2011, Charlie Robbins & the Contributors 5 | * MIT LICENSE 6 | * 7 | */ 8 | 9 | var assert = require('assert'), 10 | utile = require('../../lib'); 11 | 12 | var macros = exports; 13 | 14 | macros.assertReadCorrectJson = function (obj) { 15 | assert.isObject(obj); 16 | utile.deepEqual(obj, { 17 | hello: 'World', 18 | 'I am': ['the utile module'], 19 | thisMakesMe: { 20 | really: 1337, 21 | 'right?': true 22 | } 23 | }); 24 | }; 25 | 26 | macros.assertDirectoryRequired = function (obj) { 27 | assert.isObject(obj); 28 | utile.deepEqual(obj, { 29 | directory: { 30 | me: 'directory/index.js' 31 | }, 32 | helloWorld: { 33 | me: 'helloWorld.js' 34 | } 35 | }); 36 | }; 37 | 38 | -------------------------------------------------------------------------------- /test/random-string-test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * common-test.js : testing common.js for expected functionality 3 | * 4 | * (C) 2011, Charlie Robbins & the Contributors 5 | * 6 | */ 7 | 8 | var assert = require('assert'), 9 | vows = require('vows'), 10 | utile = require('../lib'); 11 | 12 | vows.describe('utile/randomString').addBatch({ 13 | "When using utile": { 14 | "the randomString() function": { 15 | topic: function () { 16 | return utile.randomString(); 17 | }, 18 | "should return 16 characters that are actually random by default": function (random) { 19 | assert.isString(random); 20 | assert.lengthOf(random, 16); 21 | assert.notEqual(random, utile.randomString()); 22 | }, 23 | "when you can asked for different length strings": { 24 | topic: function () { 25 | return [utile.randomString(4), utile.randomString(128)]; 26 | }, 27 | "where they actually are of length 4, 128": function (strings) { 28 | assert.isArray(strings); 29 | assert.lengthOf(strings,2); 30 | assert.isString(strings[0]); 31 | assert.isString(strings[1]); 32 | assert.lengthOf(strings[0], 4); 33 | assert.lengthOf(strings[1], 128); 34 | assert.notEqual(strings[0], strings[1].substr(0,4)); 35 | } 36 | } 37 | } 38 | } 39 | }).export(module); 40 | -------------------------------------------------------------------------------- /test/require-directory-test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * require-directory-test.js: Tests for `requireDir` and `requireDirLazy` 3 | * methods. 4 | * 5 | * (C) 2011, Charlie Robbins & the Contributors 6 | * MIT LICENSE 7 | * 8 | */ 9 | 10 | var assert = require('assert'), 11 | path = require('path'), 12 | vows = require('vows'), 13 | macros = require('./helpers/macros'), 14 | utile = require('../'); 15 | 16 | var requireFixtures = path.join(__dirname, 'fixtures', 'require-directory'); 17 | 18 | vows.describe('utile/require-directory').addBatch({ 19 | 'When using utile': { 20 | 'the `requireDir()` function': { 21 | topic: utile.requireDir(requireFixtures), 22 | 'should contain all wanted modules': macros.assertDirectoryRequired 23 | }, 24 | 'the `requireDirLazy()` function': { 25 | topic: utile.requireDirLazy(requireFixtures), 26 | 'all properties should be getters': function (obj) { 27 | assert.isObject(obj); 28 | assert.isTrue(!!Object.getOwnPropertyDescriptor(obj, 'directory').get); 29 | assert.isTrue(!!Object.getOwnPropertyDescriptor(obj, 'helloWorld').get); 30 | }, 31 | 'should contain all wanted modules': macros.assertDirectoryRequired 32 | } 33 | } 34 | }).export(module); 35 | 36 | -------------------------------------------------------------------------------- /test/utile-test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * utile-test.js: Tests for `utile` module. 3 | * 4 | * (C) 2011, Charlie Robbins & the Contributors 5 | * MIT LICENSE 6 | * 7 | */ 8 | 9 | var assert = require('assert'), 10 | vows = require('vows'), 11 | utile = require('../lib'); 12 | 13 | var obj1, obj2; 14 | 15 | obj1 = { 16 | foo: true, 17 | bar: { 18 | bar1: true, 19 | bar2: 'bar2' 20 | } 21 | }; 22 | 23 | obj2 = { 24 | baz: true, 25 | buzz: 'buzz' 26 | }; 27 | 28 | Object.defineProperties(obj2, { 29 | 30 | 'bazz': { 31 | get: function() { 32 | return 'bazz'; 33 | }, 34 | 35 | set: function() { 36 | return 'bazz'; 37 | } 38 | }, 39 | 40 | 'wat': { 41 | set: function() { 42 | return 'wat'; 43 | } 44 | } 45 | 46 | }); 47 | 48 | vows.describe('utile').addBatch({ 49 | "When using utile": { 50 | "it should have the same methods as the `util` module": function () { 51 | Object.keys(require('util')).forEach(function (fn) { 52 | assert.isFunction(utile[fn]); 53 | }); 54 | }, 55 | "it should have the correct methods defined": function () { 56 | assert.isFunction(utile.mixin); 57 | assert.isFunction(utile.clone); 58 | assert.isFunction(utile.rimraf); 59 | assert.isFunction(utile.mkdirp); 60 | assert.isFunction(utile.cpr); 61 | }, 62 | "the mixin() method": function () { 63 | var mixed = utile.mixin({}, obj1, obj2); 64 | assert.isTrue(mixed.foo); 65 | assert.isObject(mixed.bar); 66 | assert.isTrue(mixed.baz); 67 | assert.isString(mixed.buzz); 68 | assert.isTrue(!!Object.getOwnPropertyDescriptor(mixed, 'bazz').get); 69 | assert.isTrue(!!Object.getOwnPropertyDescriptor(mixed, 'bazz').set); 70 | assert.isTrue(!!Object.getOwnPropertyDescriptor(mixed, 'wat').set); 71 | assert.isString(mixed.bazz); 72 | }, 73 | "the clone() method": function () { 74 | var clone = utile.clone(obj1); 75 | assert.isTrue(clone.foo); 76 | assert.isObject(clone.bar); 77 | assert.notStrictEqual(obj1, clone); 78 | }, 79 | "the createPath() method": function () { 80 | var x = {}, 81 | r = Math.random(); 82 | 83 | utile.createPath(x, ['a','b','c'], r) 84 | assert.equal(x.a.b.c, r) 85 | }, 86 | "the capitalize() method": function () { 87 | assert.isFunction(utile.capitalize); 88 | assert.equal(utile.capitalize('bullet'), 'Bullet'); 89 | assert.equal(utile.capitalize('bullet_train'), 'BulletTrain'); 90 | }, 91 | "the escapeRegExp() method": function () { 92 | var ans = "\\/path\\/to\\/resource\\.html\\?search=query"; 93 | assert.isFunction(utile.escapeRegExp); 94 | assert.equal(utile.escapeRegExp('/path/to/resource.html?search=query'), ans); 95 | }, 96 | "the underscoreToCamel() method": function () { 97 | var obj = utile.underscoreToCamel({ 98 | key_with_underscore: { 99 | andNested: 'values', 100 | several: [1, 2, 3], 101 | nested_underscores: true 102 | }, 103 | just_one: 'underscore' 104 | }); 105 | 106 | assert.isObject(obj.keyWithUnderscore); 107 | assert.isString(obj.justOne); 108 | assert.isTrue(obj.keyWithUnderscore.nestedUnderscores); 109 | }, 110 | "the camelToUnderscore() method": function () { 111 | var obj = utile.camelToUnderscore({ 112 | keyWithCamel: { 113 | andNested: 'values', 114 | several: [1, 2, 3], 115 | nestedCamel: true 116 | }, 117 | justOne: 'camel' 118 | }); 119 | 120 | assert.isObject(obj.key_with_camel); 121 | assert.isString(obj.just_one); 122 | assert.isTrue(obj.key_with_camel.nested_camel); 123 | } 124 | } 125 | }).export(module); 126 | 127 | --------------------------------------------------------------------------------