UNPKG

9.12 kBMarkdownView Raw
1# lazy-ass
2
3> Lazy assertions without performance penalty
4
5[![NPM][lazy-ass-icon] ][lazy-ass-url]
6
7[![Build status][lazy-ass-ci-image] ][lazy-ass-ci-url]
8[![manpm](https://img.shields.io/badge/manpm-compatible-3399ff.svg)](https://github.com/bahmutov/manpm)
9[![dependencies][lazy-ass-dependencies-image] ][lazy-ass-dependencies-url]
10[![devdependencies][lazy-ass-devdependencies-image] ][lazy-ass-devdependencies-url]
11
12[![semantic-release][semantic-image] ][semantic-url]
13[![Coverage Status][lazy-ass-coverage-image]][lazy-ass-coverage-url]
14[![Codacy][lazy-ass-codacy-image]][lazy-ass-codacy-url]
15[![Code Climate][lazy-ass-code-climate-image]][lazy-ass-code-climate-url]
16
17[Demo](http://glebbahmutov.com/lazy-ass/)
18
19Is the current code breaking dependencies if released?
20[![Dont-break][circle-ci-image] ][circle-ci-url] - checks using
21[dont-break](https://github.com/bahmutov/dont-break)
22[circle-ci-image]: https://circleci.com/gh/bahmutov/lazy-ass.svg?style=svg
23[circle-ci-url]: https://circleci.com/gh/bahmutov/lazy-ass
24
25## Example
26
27Regular assertions evaluate all arguments and concatenate message
28EVERY time, even if the condition is true.
29
30```js
31console.assert(typeof foo === 'object',
32 'expected ' + JSON.stringify(foo, null, 2) + ' to be an object');
33```
34
35Lazy assertion function evaluates its arguments and forms
36a message ONLY IF the condition is false
37
38```js
39lazyAss(typeof foo === 'object', 'expected', foo, 'to be an object');
40```
41
42Concatenates strings, stringifies objects, calls functions - only if
43condition is false.
44
45```js
46function environment() {
47 // returns string
48}
49var user = {} // an object
50lazyAsync(condition, 'something went wrong for', user, 'in', environment);
51// throws an error with message equivalent of
52// 'something went wrong for ' + JSON.stringify(user) + ' in ' + environment()
53```
54
55## Why?
56
57* Passing an object reference to a function is about
58[2000-3000 times faster](http://jsperf.com/object-json-stringify)
59than serializing an object and passing it as a string.
60* Concatenating 2 strings before passing to a function is about
61[30% slower](http://jsperf.com/string-concat-vs-pass-string-reference)
62than passing 2 separate strings.
63
64## Install
65
66Node: `npm install lazy-ass --save` then `var la = require('lazy-ass');`.
67You can attach the methods to the global object using
68`require('lazy-ass').globalRegister();`.
69
70Browser: `bower install lazy-ass --save`, include `index.js`,
71attaches functions `lazyAss` and `la` to `window` object.
72
73## Notes
74
75You can pass as many arguments to *lazyAss* after the condition. The condition
76will be evaluated every time (this is required for any assertion). The rest of arguments
77will be concatenated according to rules
78
79* string will be left unchanged.
80* function will be called and its output will be concatenated.
81* any array or object will be JSON stringified.
82
83There will be single space between the individual parts.
84
85## Lazy async assertions
86
87Sometimes you do not want to throw an error synchronously, breaking the entire
88execution stack. Instead you can throw an error asynchronously using `lazyAssync`,
89which internally implements logic like this:
90
91```js
92if (!condition) {
93 setTimeout(function () {
94 throw new Error('Conditions is false!');
95 }, 0);
96}
97```
98
99This allows the execution to continue, while your global error handler (like
100my favorite [Sentry](http://glebbahmutov.com/blog/know-unknown-unknowns-with-sentry/))
101can still forward the error with all specified information to your server.
102
103```js
104lazyAss.async(false, 'foo');
105console.log('after assync');
106// output
107after assync
108Uncaught Error: foo
109```
110
111In this case, there is no meaningful error stack, so use good message
112arguments - there is no performance penalty!
113
114## Rethrowing errors
115
116If the condition itself is an instance of Error, it is simply rethrown (synchronously or
117asynchronously).
118
119```js
120lazyAss(new Error('foo'));
121// Uncaught Error: foo
122```
123
124Useful to make sure errors in the promise chains are
125[not silently ignored](https://glebbahmutov.com/blog/why-promises-need-to-be-done/).
126
127For example, a rejected promise below this will be ignored.
128
129```js
130var p = new Promise(function (resolve, reject) {
131 reject(new Error('foo'));
132});
133p.then(...);
134```
135
136We can catch it and rethrow it *synchronously*, but it will be ignored too (same way,
137only one step further)
138
139```js
140var p = new Promise(function (resolve, reject) {
141 reject(new Error('foo'));
142});
143p.then(..., lazyAss);
144```
145
146But we can actually trigger global error if we rethrow the error *asynchronously*
147
148```js
149var p = new Promise(function (resolve, reject) {
150 reject(new Error('foo'));
151});
152p.then(..., lazyAssync);
153// Uncaught Error: foo
154```
155
156## Predicate function as a condition
157
158Typically, JavaScript evaluates the condition expression first, then calls *lazyAss*.
159This means the function itself sees only the true / false result, and not the expression
160itself. This makes makes the error messages cryptic
161
162 lazyAss(2 + 2 === 5);
163 // Error
164
165We usually get around this by giving at least one additional message argument to
166explain the condition tested
167
168 lazyAss(2 + 2 === 5, 'addition')
169 // Error: addition
170
171*lazyAss* has a better solution: if you give a function that evaluates the condition
172expression, if the function returns false, the error message will include the source
173of the function, making the extra arguments unnecessary
174
175 lazyAss(function () { return 2 + 2 === 5; });
176 // Error: function () { return 2 + 2 === 5; }
177
178The condition function has access to any variables in the scope, making it extremely
179powerful
180
181 var foo = 2, bar = 2;
182 lazyAss(function () { return foo + bar === 5; });
183 // Error: function () { return foo + bar === 5; }
184
185In practical terms, I recommend using separate predicates function and
186passing relevant values to the *lazyAss* function. Remember, there is no performance
187penalty!
188
189 var foo = 2, bar = 2;
190 function isValidPair() {
191 return foo + bar === 5;
192 }
193 lazyAss(isValidPair, 'foo', foo, 'bar', bar);
194 // Error: function isValidPair() {
195 // return foo + bar === 5;
196 // } foo 2 bar 2
197
198## Testing
199
200This library is fully tested under Node and inside browser environment (CasperJs).
201I described how one can test asynchronous assertion throwing in your own projects
202using Jasmine in [a blog post](http://glebbahmutov.com/blog/testing-async-lazy-assertion/).
203
204### Small print
205
206Author: Gleb Bahmutov © 2014
207
208* [@bahmutov](https://twitter.com/bahmutov)
209* [glebbahmutov.com](http://glebbahmutov.com)
210* [blog](http://glebbahmutov.com/blog)
211
212License: MIT - do anything with the code, but don't blame me if it does not work.
213
214Spread the word: tweet, star on github, etc.
215
216Support: if you find any problems with this module, email / tweet /
217[open issue](https://github.com/bahmutov/lazy-ass/issues) on Github
218
219## MIT License
220
221Copyright (c) 2014 Gleb Bahmutov
222
223Permission is hereby granted, free of charge, to any person
224obtaining a copy of this software and associated documentation
225files (the "Software"), to deal in the Software without
226restriction, including without limitation the rights to use,
227copy, modify, merge, publish, distribute, sublicense, and/or sell
228copies of the Software, and to permit persons to whom the
229Software is furnished to do so, subject to the following
230conditions:
231
232The above copyright notice and this permission notice shall be
233included in all copies or substantial portions of the Software.
234
235THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
236EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
237OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
238NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
239HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
240WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
241FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
242OTHER DEALINGS IN THE SOFTWARE.
243
244[lazy-ass-icon]: https://nodei.co/npm/lazy-ass.svg?downloads=true
245[lazy-ass-url]: https://npmjs.org/package/lazy-ass
246[lazy-ass-ci-image]: https://travis-ci.org/bahmutov/lazy-ass.svg?branch=master
247[lazy-ass-ci-url]: https://travis-ci.org/bahmutov/lazy-ass
248[lazy-ass-coverage-image]: https://coveralls.io/repos/bahmutov/lazy-ass/badge.svg
249[lazy-ass-coverage-url]: https://coveralls.io/r/bahmutov/lazy-ass
250[lazy-ass-code-climate-image]: https://codeclimate.com/github/bahmutov/lazy-ass/badges/gpa.svg
251[lazy-ass-code-climate-url]: https://codeclimate.com/github/bahmutov/lazy-ass
252[lazy-ass-codacy-image]: https://www.codacy.com/project/badge/b60a0810c9af4fe4b2ae685932dbbdb8
253[lazy-ass-codacy-url]: https://www.codacy.com/public/bahmutov/lazy-ass.git
254[lazy-ass-dependencies-image]: https://david-dm.org/bahmutov/lazy-ass.svg
255[lazy-ass-dependencies-url]: https://david-dm.org/bahmutov/lazy-ass
256[lazy-ass-devdependencies-image]: https://david-dm.org/bahmutov/lazy-ass/dev-status.svg
257[lazy-ass-devdependencies-url]: https://david-dm.org/bahmutov/lazy-ass#info=devDependencies
258[semantic-image]: https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg
259[semantic-url]: https://github.com/semantic-release/semantic-release