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 |
|
18 | Regular assertions evaluate all arguments and concatenate message
|
19 | EVERY time, even if the condition is true.
|
20 |
|
21 | ```js
|
22 | console.assert(typeof foo === 'object',
|
23 | 'expected ' + JSON.stringify(foo, null, 2) + ' to be an object');
|
24 | ```
|
25 |
|
26 | Lazy assertion function evaluates its arguments and forms
|
27 | a message ONLY IF the condition is false
|
28 |
|
29 | ```js
|
30 | lazyAss(typeof foo === 'object', 'expected', foo, 'to be an object');
|
31 | ```
|
32 |
|
33 | Concatenates strings, stringifies objects, calls functions - only if
|
34 | condition is false.
|
35 |
|
36 | ```js
|
37 | function environment() {
|
38 | // returns string
|
39 | }
|
40 | var user = {} // an object
|
41 | lazyAsync(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)
|
50 | than 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)
|
53 | than passing 2 separate strings.
|
54 |
|
55 | ## Install
|
56 |
|
57 | Node: `npm install lazy-ass --save` then `require('lazy-ass');`,
|
58 | attaches functions `lazyAss` and `la` to `global` object.
|
59 |
|
60 | Browser: `bower install lazy-ass --save`, include `index.js`,
|
61 | attaches functions `lazyAss` and `la` to `window` object.
|
62 |
|
63 | ## Notes
|
64 |
|
65 | You can pass as many arguments to *lazyAss* after the condition. The condition
|
66 | will be evaluated every time (this is required for any assertion). The rest of arguments
|
67 | will 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 |
|
73 | There will be single space between the individual parts.
|
74 |
|
75 | ## Lazy async assertions
|
76 |
|
77 | Sometimes you do not want to throw an error synchronously, breaking the entire
|
78 | execution stack. Instead you can throw an error asynchronously using `lazyAssync`,
|
79 | which internally implements logic like this:
|
80 |
|
81 | ```js
|
82 | if (!condition) {
|
83 | setTimeout(function () {
|
84 | throw new Error('Conditions is false!');
|
85 | }, 0);
|
86 | }
|
87 | ```
|
88 |
|
89 | This allows the execution to continue, while your global error handler (like
|
90 | my favorite [Sentry](http://glebbahmutov.com/blog/know-unknown-unknowns-with-sentry/))
|
91 | can still forward the error with all specified information to your server.
|
92 |
|
93 | ```js
|
94 | lazyAssync(false, 'foo');
|
95 | console.log('after assync');
|
96 | // output
|
97 | after assync
|
98 | Uncaught Error: foo
|
99 | ```
|
100 |
|
101 | In this case, there is no meaningful error stack, so use good message
|
102 | arguments - there is no performance penalty!
|
103 |
|
104 | ## Predicate function as a condition
|
105 |
|
106 | Typically, JavaScript evaluates the condition expression first, then calls *lazyAss*.
|
107 | This means the function itself sees only the true / false result, and not the expression
|
108 | itself. This makes makes the error messages cryptic
|
109 |
|
110 | lazyAss(2 + 2 === 5);
|
111 | // Error
|
112 |
|
113 | We usually get around this by giving at least one additional message argument to
|
114 | explain 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
|
120 | expression, if the function returns false, the error message will include the source
|
121 | of the function, making the extra arguments unnecessary
|
122 |
|
123 | lazyAss(function () { return 2 + 2 === 5; });
|
124 | // Error: function () { return 2 + 2 === 5; }
|
125 |
|
126 | The condition function has access to any variables in the scope, making it extremely
|
127 | powerful
|
128 |
|
129 | var foo = 2, bar = 2;
|
130 | lazyAss(function () { return foo + bar === 5; });
|
131 | // Error: function () { return foo + bar === 5; }
|
132 |
|
133 | In practical terms, I recommend using separate predicates function and
|
134 | passing relevant values to the *lazyAss* function. Remember, there is no performance
|
135 | penalty!
|
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 |
|
148 | This library is fully tested under Node and inside browser environment (CasperJs).
|
149 | I described how one can test asynchronous assertion throwing in your own projects
|
150 | using Jasmine in [a blog post](http://glebbahmutov.com/blog/testing-async-lazy-assertion/).
|
151 |
|
152 | ### Small print
|
153 |
|
154 | Author: Gleb Bahmutov © 2014
|
155 |
|
156 | * [@bahmutov](https://twitter.com/bahmutov)
|
157 | * [glebbahmutov.com](http://glebbahmutov.com)
|
158 | * [blog](http://glebbahmutov.com/blog)
|
159 |
|
160 | License: MIT - do anything with the code, but don't blame me if it does not work.
|
161 |
|
162 | Spread the word: tweet, star on github, etc.
|
163 |
|
164 | Support: 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 |
|
169 | Copyright (c) 2014 Gleb Bahmutov
|
170 |
|
171 | Permission is hereby granted, free of charge, to any person
|
172 | obtaining a copy of this software and associated documentation
|
173 | files (the "Software"), to deal in the Software without
|
174 | restriction, including without limitation the rights to use,
|
175 | copy, modify, merge, publish, distribute, sublicense, and/or sell
|
176 | copies of the Software, and to permit persons to whom the
|
177 | Software is furnished to do so, subject to the following
|
178 | conditions:
|
179 |
|
180 | The above copyright notice and this permission notice shall be
|
181 | included in all copies or substantial portions of the Software.
|
182 |
|
183 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
184 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
185 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
186 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
187 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
188 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
189 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
190 | OTHER 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
|