1 ![hoek Logo](https://raw.github.com/hapijs/hoek/master/images/hoek.png)
3 Utility methods for the hapi ecosystem. This module is not intended to solve every problem for everyone, but rather as a central place to store hapi-specific methods. If you're looking for a general purpose utility module, check out [lodash](https://github.com/lodash/lodash) or [underscore](https://github.com/jashkenas/underscore).
5 [![Build Status](https://secure.travis-ci.org/hapijs/hoek.svg)](http://travis-ci.org/hapijs/hoek)
7 Lead Maintainer: [Nathan LaFreniere](https://github.com/nlf)
11 * [Introduction](#introduction "Introduction")
12 * [Object](#object "Object")
13 * [clone](#cloneobj "clone")
14 * [cloneWithShallow](#clonewithshallowobj-keys "cloneWithShallow")
15 * [merge](#mergetarget-source-isnulloverride-ismergearrays "merge")
16 * [applyToDefaults](#applytodefaultsdefaults-options-isnulloverride "applyToDefaults")
17 * [applyToDefaultsWithShallow](#applytodefaultswithshallowdefaults-options-keys "applyToDefaultsWithShallow")
18 * [deepEqual](#deepequala-b "deepEqual")
19 * [unique](#uniquearray-key "unique")
20 * [mapToObject](#maptoobjectarray-key "mapToObject")
21 * [intersect](#intersectarray1-array2 "intersect")
22 * [contain](#containref-values-options "contain")
23 * [flatten](#flattenarray-target "flatten")
24 * [reach](#reachobj-chain-options "reach")
25 * [reachTemplate](#reachtemplateobj-template-options "reachTemplate")
26 * [transform](#transformobj-transform-options "transform")
27 * [shallow](#shallowobj "shallow")
28 * [stringify](#stringifyobj "stringify")
29 * [Timer](#timer "Timer")
30 * [Bench](#bench "Bench")
31 * [Binary Encoding/Decoding](#binary-encodingdecoding "Binary Encoding/Decoding")
32 * [base64urlEncode](#base64urlencodevalue "binary64urlEncode")
33 * [base64urlDecode](#base64urldecodevalue "binary64urlDecode")
34 * [Escaping Characters](#escaping-characters "Escaping Characters")
35 * [escapeHtml](#escapehtmlstring "escapeHtml")
36 * [escapeHeaderAttribute](#escapeheaderattributeattribute "escapeHeaderAttribute")
37 * [escapeRegex](#escaperegexstring "escapeRegex")
38 * [Errors](#errors "Errors")
39 * [assert](#assertcondition-message "assert")
40 * [abort](#abortmessage "abort")
41 * [displayStack](#displaystackslice "displayStack")
42 * [callStack](#callstackslice "callStack")
43 * [Function](#function "Function")
44 * [nextTick](#nexttickfn "nextTick")
45 * [once](#oncefn "once")
46 * [ignore](#ignore "ignore")
47 * [Miscellaneous](#miscellaneous "Miscellaneous")
48 * [uniqueFilename](#uniquefilenamepath-extension "uniqueFilename")
49 * [isAbsolutePath](#isabsolutepathpath-platform "isAbsolutePath")
50 * [isInteger](#isintegervalue "isInteger")
56 The *Hoek* library contains some common functions used within the hapi ecosystem. It comes with useful methods for Arrays (clone, merge, applyToDefaults), Objects (removeKeys, copy), Asserting and more.
58 For example, to use Hoek to set configuration with default options:
60 var Hoek = require('hoek');
62 var default = {url : "www.github.com", port : "8000", debug : true};
64 var config = Hoek.applyToDefaults(default, {port : "3000", admin : true});
66 // In this case, config would be { url: 'www.github.com', port: '3000', debug: true, admin: true }
69 Under each of the sections (such as Array), there are subsections which correspond to Hoek methods. Each subsection will explain how to use the corresponding method. In each js excerpt below, the `var Hoek = require('hoek');` is omitted for brevity.
73 Hoek provides several helpful methods for objects and arrays.
77 This method is used to clone an object or an array. A *deep copy* is made (duplicates everything, including values that are objects, as well as non-enumerable properties).
92 var copy = Hoek.clone(nestedObj);
96 console.log(copy.y); // results in 'y'
97 console.log(nestedObj.x.b); // results in 123456
98 console.log(copy.x.b); // results in 100
101 ### cloneWithShallow(obj, keys)
102 keys is an array of key names to shallow copy
104 This method is also used to clone an object or array, however any keys listed in the `keys` array are shallow copied while those not listed are deep copied.
119 var copy = Hoek.cloneWithShallow(nestedObj, ['x']);
123 console.log(copy.y); // results in 'y'
124 console.log(nestedObj.x.b); // results in 100
125 console.log(copy.x.b); // results in 100
128 ### merge(target, source, isNullOverride, isMergeArrays)
129 isNullOverride, isMergeArrays default to true
131 Merge all the properties of source into target, source wins in conflict, and by default null and undefined from source are applied.
132 Merge is destructive where the target is modified. For non destructive merge, use `applyToDefaults`.
137 var target = {a: 1, b : 2};
138 var source = {a: 0, c: 5};
139 var source2 = {a: null, c: 5};
141 Hoek.merge(target, source); // results in {a: 0, b: 2, c: 5}
142 Hoek.merge(target, source2); // results in {a: null, b: 2, c: 5}
143 Hoek.merge(target, source2, false); // results in {a: 1, b: 2, c: 5}
145 var targetArray = [1, 2, 3];
146 var sourceArray = [4, 5];
148 Hoek.merge(targetArray, sourceArray); // results in [1, 2, 3, 4, 5]
149 Hoek.merge(targetArray, sourceArray, true, false); // results in [4, 5]
152 ### applyToDefaults(defaults, options, isNullOverride)
153 isNullOverride defaults to false
155 Apply options to a copy of the defaults
159 var defaults = { host: "localhost", port: 8000 };
160 var options = { port: 8080 };
162 var config = Hoek.applyToDefaults(defaults, options); // results in { host: "localhost", port: 8080 }
165 Apply options with a null value to a copy of the defaults
169 var defaults = { host: "localhost", port: 8000 };
170 var options = { host: null, port: 8080 };
172 var config = Hoek.applyToDefaults(defaults, options, true); // results in { host: null, port: 8080 }
175 ### applyToDefaultsWithShallow(defaults, options, keys)
176 keys is an array of key names to shallow copy
178 Apply options to a copy of the defaults. Keys specified in the last parameter are shallow copied from options instead of merged.
190 var options = { server: { port: 8080 } };
192 var config = Hoek.applyToDefaultsWithShallow(defaults, options, ['server']); // results in { server: { port: 8080 }, name: 'example' }
195 ### deepEqual(b, a, [options])
197 Performs a deep comparison of the two values including support for circular dependencies, prototype, and properties. To skip prototype comparisons, use `options.prototype = false`
200 Hoek.deepEqual({ a: [1, 2], b: 'string', c: { d: true } }, { a: [1, 2], b: 'string', c: { d: true } }); //results in true
201 Hoek.deepEqual(Object.create(null), {}, { prototype: false }); //results in true
202 Hoek.deepEqual(Object.create(null), {}); //results in false
205 ### unique(array, key)
207 Remove duplicate items from Array
211 var array = [1, 2, 2, 3, 3, 4, 5, 6];
213 var newArray = Hoek.unique(array); // results in [1,2,3,4,5,6]
215 array = [{id: 1}, {id: 1}, {id: 2}];
217 newArray = Hoek.unique(array, "id"); // results in [{id: 1}, {id: 2}]
220 ### mapToObject(array, key)
222 Convert an Array into an Object
227 var newObject = Hoek.mapToObject(array); // results in [{"1": true}, {"2": true}, {"3": true}]
229 array = [{id: 1}, {id: 2}];
230 newObject = Hoek.mapToObject(array, "id"); // results in [{"id": 1}, {"id": 2}]
233 ### intersect(array1, array2)
235 Find the common unique items in two arrays
239 var array1 = [1, 2, 3];
240 var array2 = [1, 4, 5];
242 var newArray = Hoek.intersect(array1, array2); // results in [1]
245 ### contain(ref, values, [options])
247 Tests if the reference value contains the provided values where:
248 - `ref` - the reference string, array, or object.
249 - `values` - a single or array of values to find within the `ref` value. If `ref` is an object, `values` can be a key name,
250 an array of key names, or an object with key-value pairs to compare.
251 - `options` - an optional object with the following optional settings:
252 - `deep` - if `true`, performed a deep comparison of the values.
253 - `once` - if `true`, allows only one occurrence of each value.
254 - `only` - if `true`, does not allow values not explicitly listed.
255 - `part` - if `true`, allows partial match of the values (at least one must always match).
257 Note: comparing a string to overlapping values will result in failed comparison (e.g. `contain('abc', ['ab', 'bc'])`).
258 Also, if an object key's value does not match the provided value, `false` is returned even when `part` is specified.
261 Hoek.contain('aaa', 'a', { only: true }); // true
262 Hoek.contain([{ a: 1 }], [{ a: 1 }], { deep: true }); // true
263 Hoek.contain([1, 2, 2], [1, 2], { once: true }); // false
264 Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, d: 4 }, { part: true }); // true
267 ### flatten(array, [target])
273 var array = [1, [2, 3]];
275 var flattenedArray = Hoek.flatten(array); // results in [1, 2, 3]
280 flattenedArray = Hoek.flatten(array, target); // results in [4, [5], 1, 2, 3]
283 ### reach(obj, chain, [options])
285 Converts an object key chain string to reference
287 - `options` - optional settings
288 - `separator` - string to split chain path on, defaults to '.'
289 - `default` - value to return if the path or value is not present, default is `undefined`
290 - `strict` - if `true`, will throw an error on missing member, default is `false`
291 - `functions` - if `true` allow traversing functions for properties. `false` will throw an error if a function is part of the chain.
293 A chain including negative numbers will work like negative indices on an
296 If chain is `null`, `undefined` or `false`, the object itself will be returned.
301 var obj = {a : {b : { c : 1}}};
303 Hoek.reach(obj, chain); // returns 1
305 var chain = 'a.b.-1';
306 var obj = {a : {b : [2,3,6]}};
308 Hoek.reach(obj, chain); // returns 6
311 ### reachTemplate(obj, template, [options])
313 Replaces string parameters (`{name}`) with their corresponding object key values by applying the
314 (`reach()`)[#reachobj-chain-options] method where:
316 - `obj` - the context object used for key lookup.
317 - `template` - a string containing `{}` parameters.
318 - `options` - optional (`reach()`)[#reachobj-chain-options] options.
323 var obj = {a : {b : { c : 1}}};
325 Hoek.reachTemplate(obj, '1+{a.b.c}=2'); // returns '1+1=2'
328 ### transform(obj, transform, [options])
330 Transforms an existing object into a new one based on the supplied `obj` and `transform` map. `options` are the same as the `reach` options. The first argument can also be an array of objects. In that case the method will return an array of transformed objects.
335 one: '123 main street',
342 var result = Hoek.transform(source, {
343 'person.address.lineOne': 'address.one',
344 'person.address.lineTwo': 'address.two',
346 'person.address.region': 'state'
352 // lineOne: '123 main street',
353 // lineTwo: 'PO Box 1234',
357 // title: 'Warehouse'
363 Performs a shallow copy by copying the references of all the top level children where:
364 - `obj` - the object to be copied.
367 var shallow = Hoek.shallow({ a: { b: 1 } });
372 Converts an object to string using the built-in `JSON.stringify()` method with the difference that any errors are caught
373 and reported back in the form of the returned string. Used as a shortcut for displaying information to the console (e.g. in
374 error message) without the need to worry about invalid conversion.
379 Hoek.stringify(a); // Returns '[Cannot display object: Converting circular structure to JSON]'
384 A Timer object. Initializing a new timer object sets the ts to the number of milliseconds elapsed since 1 January 1970 00:00:00 UTC.
388 var timerObj = new Hoek.Timer();
389 console.log("Time is now: " + timerObj.ts);
390 console.log("Elapsed time from initialization: " + timerObj.elapsed() + 'milliseconds');
396 Same as Timer with the exception that `ts` stores the internal node clock which is not related to `Date.now()` and cannot be used to display
397 human-readable timestamps. More accurate for benchmarking or internal timers.
399 # Binary Encoding/Decoding
401 ### base64urlEncode(value)
403 Encodes value in Base64 or URL encoding
405 ### base64urlDecode(value)
407 Decodes data in Base64 or URL encoding.
408 # Escaping Characters
410 Hoek provides convenient methods for escaping html characters. The escaped characters are as followed:
414 internals.htmlEscaped = {
424 ### escapeHtml(string)
428 var string = '<html> hey </html>';
429 var escapedString = Hoek.escapeHtml(string); // returns <html> hey </html>
432 ### escapeHeaderAttribute(attribute)
434 Escape attribute value for use in HTTP header
438 var a = Hoek.escapeHeaderAttribute('I said "go w\\o me"'); //returns I said \"go w\\o me\"
442 ### escapeRegex(string)
444 Escape string for Regex construction
448 var a = Hoek.escapeRegex('4^f$s.4*5+-_?%=#!:@|~\\/`"(>)[<]d{}s,'); // returns 4\^f\$s\.4\*5\+\-_\?%\=#\!\:@\|~\\\/`"\(>\)\[<\]d\{\}s\,
453 ### assert(condition, message)
459 Hoek.assert(a === b, 'a should equal b'); // Throws 'a should equal b'
462 Note that you may also pass an already created Error object as the second parameter, and `assert` will throw that object.
468 Hoek.assert(a === b, new Error('a should equal b')); // Throws the given error object
473 First checks if `process.env.NODE_ENV === 'test'`, and if so, throws error message. Otherwise,
474 displays most recent stack and then exits process.
478 ### displayStack(slice)
480 Displays the trace stack
484 var stack = Hoek.displayStack();
485 console.log(stack); // returns something like:
487 [ 'null (/Users/user/Desktop/hoek/test.js:4:18)',
488 'Module._compile (module.js:449:26)',
489 'Module._extensions..js (module.js:467:10)',
490 'Module.load (module.js:356:32)',
491 'Module._load (module.js:312:12)',
492 'Module.runMain (module.js:492:10)',
493 'startup.processNextTick.process._tickCallback (node.js:244:9)' ]
498 Returns a trace stack array.
502 var stack = Hoek.callStack();
503 console.log(stack); // returns something like:
505 [ [ '/Users/user/Desktop/hoek/test.js', 4, 18, null, false ],
506 [ 'module.js', 449, 26, 'Module._compile', false ],
507 [ 'module.js', 467, 10, 'Module._extensions..js', false ],
508 [ 'module.js', 356, 32, 'Module.load', false ],
509 [ 'module.js', 312, 12, 'Module._load', false ],
510 [ 'module.js', 492, 10, 'Module.runMain', false ],
514 'startup.processNextTick.process._tickCallback',
522 Returns a new function that wraps `fn` in `process.nextTick`.
526 var myFn = function () {
527 console.log('Do this later');
530 var nextFn = Hoek.nextTick(myFn);
533 console.log('Do this first');
543 Returns a new function that can be run multiple times, but makes sure `fn` is only run once.
547 var myFn = function () {
548 console.log('Ran myFn');
551 var onceFn = Hoek.once(myFn);
552 onceFn(); // results in "Ran myFn"
553 onceFn(); // results in undefined
558 A simple no-op function. It does nothing at all.
562 ### uniqueFilename(path, extension)
563 `path` to prepend with the randomly generated file name. `extension` is the optional file extension, defaults to `''`.
565 Returns a randomly generated file name at the specified `path`. The result is a fully resolved path to a file.
568 var result = Hoek.uniqueFilename('./test/modules', 'txt'); // results in "full/path/test/modules/{random}.txt"
571 ### isAbsolutePath(path, [platform])
573 Determines whether `path` is an absolute path. Returns `true` or `false`.
575 - `path` - A file path to test for whether it is absolute or not.
576 - `platform` - An optional parameter used for specifying the platform. Defaults to `process.platform`.
580 Check `value` to see if it is an integer. Returns true/false.
583 var result = Hoek.isInteger('23')