UNPKG

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