├── .gitignore
├── README.md
├── database.json
└── posts
├── behaviour-driven-development-with-javascript
└── index.md
├── harder-better-faster-stronger-lo-dash-v3
├── documentation-generator
│ ├── generator.js
│ ├── npm-debug.log
│ └── package.json
├── documentation.md
└── index.md
├── mysql-sinsert
└── index.md
├── pro-angularjs
└── index.md
├── the-definitive-guide-to-the-es7-async-functions
└── index.md
├── the-definitive-guide-to-the-javascript-generators
├── generators.gif
└── index.md
├── using-dataloader-to-batch-requests
└── index.md
└── using-mysql-in-node-js
└── index.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Contents of the http://gajus.com/blog/.
2 |
--------------------------------------------------------------------------------
/database.json:
--------------------------------------------------------------------------------
1 | {
2 | "posts": [
3 | {
4 | "id": 1,
5 | "name": "Behaviour Driven Development with Javascript",
6 | "nid": "behaviour-driven-development-with-javascript",
7 | "published_at": "2014-09-24 14:37:34"
8 | },
9 | {
10 | "id": 2,
11 | "name": "The Definitive Guide to the JavaScript Generators",
12 | "nid": "the-definitive-guide-to-the-javascript-generators",
13 | "published_at": "2014-10-04 10:58:48"
14 | },
15 | {
16 | "id": 3,
17 | "name": "Pro AngularJS",
18 | "nid": "pro-angularjs",
19 | "published_at": null
20 | },
21 | {
22 | "id": 4,
23 | "name": "Harder, Better, Faster, Stronger Lo-Dash v3",
24 | "nid": "harder-better-faster-stronger-lo-dash-v3",
25 | "published_at": "2015-01-20 14:46:53"
26 | },
27 | {
28 | "id": 6,
29 | "name": "MySQL SINSERT",
30 | "nid": "mysql-sinsert",
31 | "published_at": "2015-04-02 18:18:20"
32 | },
33 | {
34 | "id": 7,
35 | "name": "The Definitive Guide to the ES7 async functions",
36 | "nid": "the-definitive-guide-to-the-es7-async-functions",
37 | "published_at": null
38 | },
39 | {
40 | "id": 8,
41 | "name": "Using MySQL in Node.js",
42 | "nid": "using-mysql-in-node-js",
43 | "published_at": "2016-04-24 17:37:38"
44 | },
45 | {
46 | "id": 9,
47 | "name": "Using DataLoader to batch requests",
48 | "nid": "using-dataloader-to-batch-requests",
49 | "published_at": "2016-08-19 14:59:43"
50 | }
51 | ]
52 | }
53 |
--------------------------------------------------------------------------------
/posts/behaviour-driven-development-with-javascript/index.md:
--------------------------------------------------------------------------------
1 | This article is a result of extensive research about BDD in JavaScript. I have extracted the core principals and terminology, and provide practical examples that illustrate the benefits of BDD.
2 |
3 | ## What is BDD?
4 |
5 | In the 8th chapter, the author quotes various definitions of BDD. I favor the following:
6 |
7 |
8 |
9 |
[BDD] extends TDD by writing test cases in a natural language that non-programmers can read.
10 |
11 | M. Manca
12 |
13 |
14 |
15 |
16 |
BDD is a second-generation, outside-in, pull-based, multiple-stakeholder, multiple-scale, high-automation, agile methodology. It describes a cycle of interactions with well-defined outputs, resulting in the delivery of working, tested software that matters.
17 |
18 | Dan North
19 |
20 |
21 | Developers pull features from stakeholders and develop them outside-in. The features are pulled in the order of their business value. The most important feature is implemented first and it's implemented outside in. No time is wasted building unnecessary code.
22 |
23 | The evolution of a programmer that is learning TDD is a good guidance in understanding the BDD:
24 |
25 | 1. Most developers start by writing unit tests to existing code.
26 | 1. After some practise they learn about the advantages of writing the test first. They might learn about good testing practices like AAA, stubs and factories at this point.
27 | 1. They realise that TDD can be used as a design tool to discover the API of the production code.
28 | 1. Eventually they recognise that TDD is not about testing. It's about defining behavior and writing specifications that serve as living documentation. They also discover mocks and outside-in development.
29 |
30 | ## Principles of BDD
31 |
32 | ### Baby Steps
33 |
34 | Characteristic feature of BDD is using "baby steps" to achieve tight feedback loop. In practice, this means writing a single spec knowing that it will fail, writing the missing code logic, returning to the spec. This continues switching between code and test cases results in less scanning of the code.
35 |
36 | ### Red/Green/Refactor
37 |
38 | You want to achieve Red/Green/Refactor routine:
39 |
40 |
41 |
Are all specs green (after the implementation phase)?
42 |
Is one spec red (after coding another spec)?
43 |
44 |
45 | The idea is to ensure that the test really works and can catch an error.
46 |
47 |
Triangulation
48 |
49 | Instead of filling in a real implementation, you can fake it with a dummy value.
50 |
51 |
52 |
53 | You know that your implementation isn't ready, but all the specs are showing green. This implies that there is a spec example missing.
54 |
55 |
56 |
57 | You proceed until you've covered just enough test cases to produce a general solution.
58 |
59 |
60 |
61 | The idea is to gain moment and insight into how the algorithm should behave.
62 |
63 | ### Summary
64 |
65 | Behaviour Driven Development is characterized by:
66 |
67 | * Spec/Error driven coding.
68 | * Using baby-steps to achieve a "tight feedback-loop".
69 | * Follow the Red-Green-Refactor (RGR) cycle to avoid the false-positives.
70 |
71 | The result:
72 |
73 | * Software design driven by needs.
74 | * RGR cycle enhances the separation of the workload. Red is for the interface, green is for the implementation.
75 | * Specs enable code refactoring.
76 | * Automated tests that read like documentation. Prefer DAMP over DRY.
77 |
78 |
79 |
Refactoring
80 |
Altering internal code structure without affecting the external behaviour.
81 |
82 |
DAMP
83 |
Descriptive and Meaningful Phrases.
84 |
85 |
86 | ## BDD in Practice
87 |
88 | ### Using Test Doubles
89 |
90 | #### Dummies
91 |
92 | Empty object that does nothing.
93 |
94 | * Objects are passed around but never actually used. Usually they are just used to fill parameter lists.[^http://martinfowler.com/articles/mocksArentStubs.html]
95 |
96 |
97 |
98 | #### Stubs
99 |
100 | Method designed just for testing.
101 |
102 | * Stub objects provide a valid response, but it's static – no matter what input you pass in, you'll always get the same response.[^http://stackoverflow.com/a/1830000/368691]
103 | * An object that provides predefined answers to method calls.[^http://stackoverflow.com/a/5180286/368691]
104 | * Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test. Stubs may also record information about calls, such as an email gateway stub that remembers the messages it 'sent', or maybe only how many messages it "sent".[^http://martinfowler.com/articles/mocksArentStubs.html]
105 | * A stub will never cause a test to fail.[^http://ayende.com/Wiki/Rhino+Mocks+3.5.ashx]
106 | * Stub is simple fake object. It just makes sure test runs smoothly.[^http://stackoverflow.com/a/3459431/368691]
107 |
108 |
109 |
110 | #### Mocks
111 |
112 | Object designed just for testing.
113 |
114 | * Mock objects are used in mock test cases – they validate that certain methods are called on those objects.[^http://stackoverflow.com/a/1830000/368691]
115 | * An object on which you set expectations.[^http://stackoverflow.com/a/5180286/368691]
116 |
117 |
118 |
119 | ### Example Factory
120 |
121 | Method used to instantiate the SUT object with canonical values, overwriting only the properties relevant to the test case.
122 |
123 |
124 |
125 | See https://github.com/petejkim/factory-lady, Object Mother and Test Data Builders.
126 |
127 | ### SEE Pattern
128 |
129 | Every spec needs these three parts and nothing more – in this order:
130 |
131 | 1. Setup, e.g. create an object.
132 | 1. Execute, i.e. introduce change to the object state.
133 | 1. Expect, i.e. state an expectation of what should have happened.
134 |
135 | You might have also heard Given, When, Then (GWT):
136 |
137 | * Given a condition.
138 | * When I do something.
139 | * Then I expect something to happen.
140 |
141 | The two are identical. The phrasing of the latter might be easier to comprehend.
142 |
143 |
144 |
145 | As a result, your specs must adhere to the following rules:
146 |
147 | * The spec must implement not more than one execution.
148 | * The spec must abstain from several expectations.
149 | * Do not use if-statement that lead to multiple execution and expectation branches – write several specs instead.
150 |
151 | In TDD world it is known as the Arrange, Act, Assert (AAA) pattern.
152 |
153 | ## Organizing Specs
154 |
155 | The specs are organised either per Feature or per Fixture.
156 |
157 | ### Per Feature
158 |
159 | This way of organizing a spec is also referred to as "by topic".
160 |
161 | The benefit of organizing your spec per feature makes it easier to write.
162 |
163 |
164 |
165 | ### Per Fixture
166 |
167 | This way or organizing a spec is also referred to as "by example data".
168 |
169 | The benefit of organizing your spec per fixture makes the resulting spec more descriptive and easier to read. The additional benefit is because all your examples share the same example data, you need to state it only once (e.g. using beforeEach setup function).
170 |
171 |
172 |
173 | ## High and Low Level Spec
174 |
175 | BDD project usually starts with writing the outer circle (high level specs) and then proceeding the implementation of the inner circle (low level specs).
176 |
177 | ### Outside-In Development
178 |
179 | You start with the implementation of the specs that have meaning to the business using hypothetical components (aka. acceptance test):
180 |
181 |
182 |
183 | Acceptance test is a high level spec that describes a scenario from the view of an application user. In contrast to the low level spec, a high level spec does not have an SUT.
184 |
185 | The upside of the outside-in development is that you never write code that will become redundant (the requirement can be traced back to the spec). The downside is that you cannot run the test cases until the implementation is complete.
186 |
187 | * They help to become aware of bugs – all spec violations are considered a bug (defect awareness).
188 | * They are most useful when shown to stakeholders.
189 | * There are usually only a few. they don't include all special cases. their main purpose is to provide an overview of the required functionality.
190 |
191 | ### Inside-Out Development
192 |
193 | You start with the basic components that make up the application:
194 |
195 |
196 |
197 | * They help to find specific bugs (localization).
198 | * They are most useful to the developers who maintain the code later.
199 | * There are usually a lot of low level specs since they need to cover all the low level details, special and edge cases.
200 |
201 | ## Further Read
202 |
203 | This post is a result of reading "Behaviour Driven Development with JavaScript by Marco Emrich"[^http://www.amazon.com/Behaviour-Driven-Development-JavaScript-introduction-ebook/dp/B00CYMN3J2] and the subsequent research. Another grate resource, although beyond JavaScript scope, is "BDD in Action: Behavior-driven development for the whole software lifecycle"[^http://www.amazon.com/BDD-Action-Behavior-driven-development-lifecycle/dp/161729165X/].
204 |
--------------------------------------------------------------------------------
/posts/harder-better-faster-stronger-lo-dash-v3/documentation-generator/generator.js:
--------------------------------------------------------------------------------
1 | var _ = require('lodash'),
2 | Promise = require('bluebird'),
3 | request = require('request-promise'),
4 | commentParser = require('comment-parser'),
5 | fs = require('fs'),
6 | methods,
7 | lastGroup,
8 | docs = '',
9 | index = '';
10 |
11 | methods = [
12 | 'string/camelCase',
13 | 'string/capitalize',
14 | 'string/deburr',
15 | 'string/endsWith',
16 | 'string/escapeRegExp',
17 | 'string/kebabCase',
18 | 'string/pad',
19 | 'string/padLeft',
20 | 'string/padRight',
21 | 'string/repeat',
22 | 'string/snakeCase',
23 | 'string/startsWith',
24 | 'string/trim',
25 | 'string/trimLeft',
26 | 'string/trimRight',
27 | 'string/trunc',
28 | 'string/words',
29 | 'array/chunk',
30 | 'array/dropRight',
31 | 'array/dropRightWhile',
32 | 'array/dropWhile',
33 | 'array/flattenDeep',
34 | 'array/pullAt',
35 | 'array/slice',
36 | 'array/sortedLastIndex',
37 | 'array/takeRight',
38 | 'array/takeRightWhile',
39 | 'array/takeWhile',
40 | 'function/ary',
41 | 'function/before',
42 | 'function/curryRight',
43 | 'function/flow',
44 | 'function/negate',
45 | 'function/rearg',
46 | 'lang/isError',
47 | 'lang/isMatch',
48 | 'lang/isNative',
49 | 'lang/isTypedArray',
50 | 'lang/toPlainObject',
51 | 'utility/attempt',
52 | 'utility/matches',
53 | 'utility/propertyOf',
54 | 'collection/partition',
55 | 'collection/sortByAll',
56 | 'object/keysIn',
57 | 'object/valuesIn',
58 | 'chain/thru'
59 | ];
60 |
61 | docs += '# Lo-Dash v3 Documentation\n\n';
62 | docs += 'Lo-Dash v3 documentation generated from the source code.\n\n';
63 |
64 | Promise
65 | .all(methods)
66 | .map(function (name) {
67 | return Promise.props({
68 | name: name,
69 | body: request('https://raw.githubusercontent.com/lodash/lodash/es6/' + name + '.js')
70 | })
71 | })
72 | .each(function (method) {
73 | var comments,
74 | example,
75 | params,
76 | returns,
77 | body = method.body,
78 | group = method.name.split('/')[0],
79 | name = method.name.split('/')[1];
80 |
81 | comments = commentParser(body);
82 | comments = comments[comments.length - 1];
83 |
84 | if (lastGroup !== group) {
85 | docs += '## ' + group + '\n\n';
86 |
87 | index += '\n### ' + group + '\n\n';
88 | index += '| Name | Description |\n';
89 | index += '| --- | --- |\n';
90 |
91 | lastGroup = group;
92 | }
93 |
94 | index += '| [`' + name + '`](https://github.com/gajus/blog.gajus.com/blob/master/post/lodash-v3/documentation.md#' + name.toLowerCase() + ')| ' + comments.description.replace(/\n/g, ' ') + ' |\n';
95 |
96 | docs += '### ' + name + '\n\n';
97 | docs += 'https://raw.githubusercontent.com/lodash/lodash/es6/' + method.name + '.js\n\n';
98 | docs += comments.description.split('\n').join('\n\n');
99 |
100 | params = _.where(comments.tags, {tag: 'param'});
101 |
102 | if (params.length) {
103 | docs += '\n\n#### Parameters\n\n';
104 |
105 | docs += '| Name | Type | Description |\n';
106 | docs += '| --- | --- | --- |\n';
107 |
108 | params.forEach(function (param) {
109 | docs += '| `' + param.name + '` | `' + param.type + '` | ' + param.description + '|\n';
110 | });
111 | }
112 |
113 | returns = _.where(comments.tags, {tag: 'returns'});
114 |
115 | if (returns.length) {
116 | docs += '\n\n#### Returns\n\n';
117 |
118 | docs += '| Type | Description |\n';
119 | docs += '| --- | --- |\n';
120 | docs += '| `' + returns[0].type + '` | ' + returns[0].description + '|\n';
121 | }
122 |
123 | example = _.where(comments.tags, {tag: 'example'});
124 |
125 | if (example.length) {
126 | docs += '\n```js\n' + example[0].description + '\n```';
127 | }
128 |
129 | docs += '\n\n';
130 | })
131 | .then(function () {
132 | fs.writeFileSync(__dirname + '/../documentation.md', docs);
133 |
134 | console.log(index);
135 | });
136 |
--------------------------------------------------------------------------------
/posts/harder-better-faster-stronger-lo-dash-v3/documentation-generator/npm-debug.log:
--------------------------------------------------------------------------------
1 | 0 info it worked if it ends with ok
2 | 1 verbose cli [ 'node',
3 | 1 verbose cli '/usr/bin/npm',
4 | 1 verbose cli 'install',
5 | 1 verbose cli 'git+https://github.com/lodash/lodash-cli.git' ]
6 | 2 info using npm@1.3.6
7 | 3 info using node@v0.10.26
8 | 4 warn package.json documentation-generator@ No repository field.
9 | 5 warn package.json documentation-generator@ No README data
10 | 6 verbose readDependencies using package.json deps
11 | 7 verbose cache add [ 'git+https://github.com/lodash/lodash-cli.git', null ]
12 | 8 verbose cache add name=undefined spec="git+https://github.com/lodash/lodash-cli.git" args=["git+https://github.com/lodash/lodash-cli.git",null]
13 | 9 verbose parsed url { protocol: 'git+https:',
14 | 9 verbose parsed url slashes: true,
15 | 9 verbose parsed url auth: null,
16 | 9 verbose parsed url host: 'github.com',
17 | 9 verbose parsed url port: null,
18 | 9 verbose parsed url hostname: 'github.com',
19 | 9 verbose parsed url hash: null,
20 | 9 verbose parsed url search: null,
21 | 9 verbose parsed url query: null,
22 | 9 verbose parsed url pathname: '/lodash/lodash-cli.git',
23 | 9 verbose parsed url path: '/lodash/lodash-cli.git',
24 | 9 verbose parsed url href: 'git+https://github.com/lodash/lodash-cli.git' }
25 | 10 silly lockFile fee0a640-github-com-lodash-lodash-cli-git git+https://github.com/lodash/lodash-cli.git
26 | 11 verbose lock git+https://github.com/lodash/lodash-cli.git /home/gajus/.npm/fee0a640-github-com-lodash-lodash-cli-git.lock
27 | 12 verbose addRemoteGit [ 'https://github.com/lodash/lodash-cli.git', 'master' ]
28 | 13 verbose git clone https://github.com/lodash/lodash-cli.git Initialized empty Git repository in /home/gajus/.npm/_git-remotes/https-github-com-lodash-lodash-cli-git-88736ad0/
29 | 14 verbose git fetch -a origin (https://github.com/lodash/lodash-cli.git)
30 | 15 verbose git rev-list -n1 master f59acdc633d22d739e14a003621501a79f019f88
31 | 16 verbose resolved git url git+https://github.com/lodash/lodash-cli.git#f59acdc633d22d739e14a003621501a79f019f88
32 | 17 verbose tar unpack /home/gajus/tmp/npm-18894-R6zM1pk-/1421765463792-0.8938885827083141/tmp.tgz
33 | 18 silly lockFile 021425da-63792-0-8938885827083141-package tar:///home/gajus/tmp/npm-18894-R6zM1pk-/1421765463792-0.8938885827083141/package
34 | 19 verbose lock tar:///home/gajus/tmp/npm-18894-R6zM1pk-/1421765463792-0.8938885827083141/package /home/gajus/.npm/021425da-63792-0-8938885827083141-package.lock
35 | 20 silly lockFile 0a0a8e42-63792-0-8938885827083141-tmp-tgz tar:///home/gajus/tmp/npm-18894-R6zM1pk-/1421765463792-0.8938885827083141/tmp.tgz
36 | 21 verbose lock tar:///home/gajus/tmp/npm-18894-R6zM1pk-/1421765463792-0.8938885827083141/tmp.tgz /home/gajus/.npm/0a0a8e42-63792-0-8938885827083141-tmp-tgz.lock
37 | 22 silly gunzTarPerm modes [ '755', '644' ]
38 | 23 silly gunzTarPerm extractEntry
39 | 24 silly gunzTarPerm modified mode [ '', 509, 493 ]
40 | 25 silly gunzTarPerm extractEntry .gitattributes
41 | 26 silly gunzTarPerm modified mode [ '.gitattributes', 436, 420 ]
42 | 27 silly gunzTarPerm extractEntry .gitignore
43 | 28 silly gunzTarPerm modified mode [ '.gitignore', 436, 420 ]
44 | 29 silly gunzTarPerm extractEntry .travis.yml
45 | 30 silly gunzTarPerm modified mode [ '.travis.yml', 436, 420 ]
46 | 31 silly gunzTarPerm extractEntry CONTRIBUTING.md
47 | 32 silly gunzTarPerm modified mode [ 'CONTRIBUTING.md', 436, 420 ]
48 | 33 silly gunzTarPerm extractEntry LICENSE.txt
49 | 34 silly gunzTarPerm modified mode [ 'LICENSE.txt', 436, 420 ]
50 | 35 silly gunzTarPerm extractEntry README.md
51 | 36 silly gunzTarPerm modified mode [ 'README.md', 436, 420 ]
52 | 37 silly gunzTarPerm extractEntry bin/
53 | 38 silly gunzTarPerm modified mode [ 'bin/', 509, 493 ]
54 | 39 silly gunzTarPerm extractEntry bin/lodash
55 | 40 silly gunzTarPerm modified mode [ 'bin/lodash', 436, 420 ]
56 | 41 silly gunzTarPerm extractEntry lib/
57 | 42 silly gunzTarPerm modified mode [ 'lib/', 509, 493 ]
58 | 43 silly gunzTarPerm extractEntry lib/minify.js
59 | 44 silly gunzTarPerm modified mode [ 'lib/minify.js', 509, 493 ]
60 | 45 silly gunzTarPerm extractEntry lib/post-compile.js
61 | 46 silly gunzTarPerm modified mode [ 'lib/post-compile.js', 436, 420 ]
62 | 47 silly gunzTarPerm extractEntry lib/pre-compile.js
63 | 48 silly gunzTarPerm modified mode [ 'lib/pre-compile.js', 436, 420 ]
64 | 49 silly gunzTarPerm extractEntry lib/util.js
65 | 50 silly gunzTarPerm modified mode [ 'lib/util.js', 509, 493 ]
66 | 51 silly gunzTarPerm extractEntry npm-shrinkwrap.json
67 | 52 silly gunzTarPerm modified mode [ 'npm-shrinkwrap.json', 436, 420 ]
68 | 53 silly gunzTarPerm extractEntry package.json
69 | 54 silly gunzTarPerm modified mode [ 'package.json', 436, 420 ]
70 | 55 silly gunzTarPerm extractEntry template/
71 | 56 silly gunzTarPerm modified mode [ 'template/', 509, 493 ]
72 | 57 silly gunzTarPerm extractEntry template/license.jst
73 | 58 silly gunzTarPerm modified mode [ 'template/license.jst', 436, 420 ]
74 | 59 silly gunzTarPerm extractEntry template/package.jst
75 | 60 silly gunzTarPerm modified mode [ 'template/package.jst', 436, 420 ]
76 | 61 silly gunzTarPerm extractEntry template/readme.jst
77 | 62 silly gunzTarPerm modified mode [ 'template/readme.jst', 436, 420 ]
78 | 63 silly gunzTarPerm extractEntry test/
79 | 64 silly gunzTarPerm modified mode [ 'test/', 509, 493 ]
80 | 65 silly gunzTarPerm extractEntry test/fixture/
81 | 66 silly gunzTarPerm modified mode [ 'test/fixture/', 509, 493 ]
82 | 67 silly gunzTarPerm extractEntry test/fixture/a.jst
83 | 68 silly gunzTarPerm modified mode [ 'test/fixture/a.jst', 436, 420 ]
84 | 69 silly gunzTarPerm extractEntry test/fixture/b.jst
85 | 70 silly gunzTarPerm modified mode [ 'test/fixture/b.jst', 436, 420 ]
86 | 71 silly gunzTarPerm extractEntry test/fixture/c.jst
87 | 72 silly gunzTarPerm modified mode [ 'test/fixture/c.jst', 436, 420 ]
88 | 73 silly gunzTarPerm extractEntry test/fixture/c/
89 | 74 silly gunzTarPerm modified mode [ 'test/fixture/c/', 509, 493 ]
90 | 75 silly gunzTarPerm extractEntry test/fixture/c/c.jst
91 | 76 silly gunzTarPerm modified mode [ 'test/fixture/c/c.jst', 436, 420 ]
92 | 77 silly gunzTarPerm extractEntry test/fixture/d.jst
93 | 78 silly gunzTarPerm modified mode [ 'test/fixture/d.jst', 436, 420 ]
94 | 79 silly gunzTarPerm extractEntry test/fixture/e.jst
95 | 80 silly gunzTarPerm modified mode [ 'test/fixture/e.jst', 436, 420 ]
96 | 81 silly gunzTarPerm extractEntry test/fixture/f.tpl
97 | 82 silly gunzTarPerm modified mode [ 'test/fixture/f.tpl', 436, 420 ]
98 | 83 silly gunzTarPerm extractEntry test/npm/
99 | 84 silly gunzTarPerm modified mode [ 'test/npm/', 509, 493 ]
100 | 85 silly gunzTarPerm extractEntry test/npm/index.js
101 | 86 silly gunzTarPerm modified mode [ 'test/npm/index.js', 436, 420 ]
102 | 87 silly gunzTarPerm extractEntry test/test.js
103 | 88 silly gunzTarPerm modified mode [ 'test/test.js', 436, 420 ]
104 | 89 silly lockFile 021425da-63792-0-8938885827083141-package tar:///home/gajus/tmp/npm-18894-R6zM1pk-/1421765463792-0.8938885827083141/package
105 | 90 silly lockFile 021425da-63792-0-8938885827083141-package tar:///home/gajus/tmp/npm-18894-R6zM1pk-/1421765463792-0.8938885827083141/package
106 | 91 silly lockFile 0a0a8e42-63792-0-8938885827083141-tmp-tgz tar:///home/gajus/tmp/npm-18894-R6zM1pk-/1421765463792-0.8938885827083141/tmp.tgz
107 | 92 silly lockFile 0a0a8e42-63792-0-8938885827083141-tmp-tgz tar:///home/gajus/tmp/npm-18894-R6zM1pk-/1421765463792-0.8938885827083141/tmp.tgz
108 | 93 verbose tar pack [ '/home/gajus/.npm/lodash-cli/3.0.0/package.tgz',
109 | 93 verbose tar pack '/home/gajus/tmp/npm-18894-R6zM1pk-/1421765463792-0.8938885827083141/package' ]
110 | 94 verbose tarball /home/gajus/.npm/lodash-cli/3.0.0/package.tgz
111 | 95 verbose folder /home/gajus/tmp/npm-18894-R6zM1pk-/1421765463792-0.8938885827083141/package
112 | 96 silly lockFile 021425da-63792-0-8938885827083141-package tar:///home/gajus/tmp/npm-18894-R6zM1pk-/1421765463792-0.8938885827083141/package
113 | 97 verbose lock tar:///home/gajus/tmp/npm-18894-R6zM1pk-/1421765463792-0.8938885827083141/package /home/gajus/.npm/021425da-63792-0-8938885827083141-package.lock
114 | 98 silly lockFile 01c44be2-npm-lodash-cli-3-0-0-package-tgz tar:///home/gajus/.npm/lodash-cli/3.0.0/package.tgz
115 | 99 verbose lock tar:///home/gajus/.npm/lodash-cli/3.0.0/package.tgz /home/gajus/.npm/01c44be2-npm-lodash-cli-3-0-0-package-tgz.lock
116 | 100 silly lockFile 021425da-63792-0-8938885827083141-package tar:///home/gajus/tmp/npm-18894-R6zM1pk-/1421765463792-0.8938885827083141/package
117 | 101 silly lockFile 021425da-63792-0-8938885827083141-package tar:///home/gajus/tmp/npm-18894-R6zM1pk-/1421765463792-0.8938885827083141/package
118 | 102 silly lockFile 01c44be2-npm-lodash-cli-3-0-0-package-tgz tar:///home/gajus/.npm/lodash-cli/3.0.0/package.tgz
119 | 103 silly lockFile 01c44be2-npm-lodash-cli-3-0-0-package-tgz tar:///home/gajus/.npm/lodash-cli/3.0.0/package.tgz
120 | 104 silly lockFile 87d2af8e-jus-npm-lodash-cli-3-0-0-package /home/gajus/.npm/lodash-cli/3.0.0/package
121 | 105 verbose lock /home/gajus/.npm/lodash-cli/3.0.0/package /home/gajus/.npm/87d2af8e-jus-npm-lodash-cli-3-0-0-package.lock
122 | 106 silly lockFile 87d2af8e-jus-npm-lodash-cli-3-0-0-package /home/gajus/.npm/lodash-cli/3.0.0/package
123 | 107 silly lockFile 87d2af8e-jus-npm-lodash-cli-3-0-0-package /home/gajus/.npm/lodash-cli/3.0.0/package
124 | 108 verbose tar unpack /home/gajus/.npm/lodash-cli/3.0.0/package.tgz
125 | 109 silly lockFile de585d58-jus-npm-lodash-cli-3-0-0-package tar:///home/gajus/.npm/lodash-cli/3.0.0/package
126 | 110 verbose lock tar:///home/gajus/.npm/lodash-cli/3.0.0/package /home/gajus/.npm/de585d58-jus-npm-lodash-cli-3-0-0-package.lock
127 | 111 silly lockFile 01c44be2-npm-lodash-cli-3-0-0-package-tgz tar:///home/gajus/.npm/lodash-cli/3.0.0/package.tgz
128 | 112 verbose lock tar:///home/gajus/.npm/lodash-cli/3.0.0/package.tgz /home/gajus/.npm/01c44be2-npm-lodash-cli-3-0-0-package-tgz.lock
129 | 113 silly gunzTarPerm modes [ '755', '644' ]
130 | 114 silly gunzTarPerm extractEntry package.json
131 | 115 silly gunzTarPerm extractEntry README.md
132 | 116 silly gunzTarPerm extractEntry LICENSE.txt
133 | 117 silly gunzTarPerm extractEntry bin/lodash
134 | 118 silly gunzTarPerm extractEntry lib/minify.js
135 | 119 silly gunzTarPerm extractEntry lib/post-compile.js
136 | 120 silly gunzTarPerm extractEntry lib/pre-compile.js
137 | 121 silly gunzTarPerm extractEntry lib/util.js
138 | 122 silly gunzTarPerm extractEntry template/license.jst
139 | 123 silly gunzTarPerm extractEntry template/package.jst
140 | 124 silly gunzTarPerm extractEntry template/readme.jst
141 | 125 silly lockFile de585d58-jus-npm-lodash-cli-3-0-0-package tar:///home/gajus/.npm/lodash-cli/3.0.0/package
142 | 126 silly lockFile de585d58-jus-npm-lodash-cli-3-0-0-package tar:///home/gajus/.npm/lodash-cli/3.0.0/package
143 | 127 silly lockFile 01c44be2-npm-lodash-cli-3-0-0-package-tgz tar:///home/gajus/.npm/lodash-cli/3.0.0/package.tgz
144 | 128 silly lockFile 01c44be2-npm-lodash-cli-3-0-0-package-tgz tar:///home/gajus/.npm/lodash-cli/3.0.0/package.tgz
145 | 129 verbose chmod /home/gajus/.npm/lodash-cli/3.0.0/package.tgz 644
146 | 130 verbose chown /home/gajus/.npm/lodash-cli/3.0.0/package.tgz [ 502, 503 ]
147 | 131 silly lockFile 88736ad0-github-com-lodash-lodash-cli-git https://github.com/lodash/lodash-cli.git
148 | 132 silly resolved [ { name: 'lodash-cli',
149 | 132 silly resolved version: '3.0.0',
150 | 132 silly resolved description: 'The Lo-Dash command-line interface.',
151 | 132 silly resolved homepage: 'https://lodash.com/custom-builds',
152 | 132 silly resolved license: 'MIT',
153 | 132 silly resolved main: 'bin/lodash',
154 | 132 silly resolved bin: { lodash: 'bin/lodash' },
155 | 132 silly resolved keywords: [ 'builder', 'compile', 'customize', 'lodash', 'util' ],
156 | 132 silly resolved author:
157 | 132 silly resolved { name: 'John-David Dalton',
158 | 132 silly resolved email: 'john.david.dalton@gmail.com',
159 | 132 silly resolved url: 'http://allyoucanleet.com/' },
160 | 132 silly resolved contributors: [ [Object], [Object], [Object], [Object], [Object] ],
161 | 132 silly resolved repository: { type: 'git', url: 'lodash/lodash-cli' },
162 | 132 silly resolved scripts: { test: 'echo "See the repository CONTRIBUTING.md for testing instructions."' },
163 | 132 silly resolved dependencies:
164 | 132 silly resolved { 'closure-compiler': '0.2.6',
165 | 132 silly resolved glob: '~4.3.5',
166 | 132 silly resolved 'lodash-compat': '3.0.0',
167 | 132 silly resolved 'uglify-js': '2.4.16' },
168 | 132 silly resolved devDependencies: { 'qunit-extras': '~1.4.1', qunitjs: '~1.16.0' },
169 | 132 silly resolved files:
170 | 132 silly resolved [ 'LICENSE.txt',
171 | 132 silly resolved 'bin/lodash',
172 | 132 silly resolved 'lib/minify.js',
173 | 132 silly resolved 'lib/pre-compile.js',
174 | 132 silly resolved 'lib/post-compile.js',
175 | 132 silly resolved 'lib/util.js',
176 | 132 silly resolved 'template/license.jst',
177 | 132 silly resolved 'template/package.jst',
178 | 132 silly resolved 'template/readme.jst' ],
179 | 132 silly resolved readme: '# lodash-cli v3.0.0\n\nThe [Lo-Dash](https://lodash.com/) command-line interface for creating custom builds & precompiling templates.\n\n## Dive in\n\nCheck out the [Lo-Dash custom builds documentation](https://lodash.com/custom-builds) for build commands & usage examples. \nThe changelog for this release is available on our [wiki](https://github.com/lodash/lodash-cli/wiki/Changelog).\n\n## Installation\n\nUsing npm:\n\n```bash\n$ {sudo -H} npm i -g npm\n$ {sudo -H} npm i -g lodash-cli\n$ lodash -h\n```\n\n## Support\n\nTested in Node.js 0.8.28 & 0.10.35.\n\n**Note:** Node.js 0.10.8-0.10.11 [have](https://github.com/joyent/node/issues/5622) [bugs](https://github.com/joyent/node/issues/5688) preventing minified builds.\n',
180 | 132 silly resolved readmeFilename: 'README.md',
181 | 132 silly resolved _id: 'lodash-cli@3.0.0',
182 | 132 silly resolved dist: { shasum: '5ed82e86540b223893bdee6f5a9b741c6d4409df' },
183 | 132 silly resolved _resolved: 'git+https://github.com/lodash/lodash-cli.git#f59acdc633d22d739e14a003621501a79f019f88',
184 | 132 silly resolved _from: 'git+https://github.com/lodash/lodash-cli.git' } ]
185 | 133 info install lodash-cli@3.0.0 into /var/www/dev/gajus kuizinas/2014 02 02 gajus.com/_blog/post/lodash-v3/documentation-generator
186 | 134 info installOne lodash-cli@3.0.0
187 | 135 info /var/www/dev/gajus kuizinas/2014 02 02 gajus.com/_blog/post/lodash-v3/documentation-generator/node_modules/lodash-cli unbuild
188 | 136 verbose tar unpack /home/gajus/.npm/lodash-cli/3.0.0/package.tgz
189 | 137 silly lockFile 7e402026-enerator-node-modules-lodash-cli tar:///var/www/dev/gajus kuizinas/2014 02 02 gajus.com/_blog/post/lodash-v3/documentation-generator/node_modules/lodash-cli
190 | 138 verbose lock tar:///var/www/dev/gajus kuizinas/2014 02 02 gajus.com/_blog/post/lodash-v3/documentation-generator/node_modules/lodash-cli /home/gajus/.npm/7e402026-enerator-node-modules-lodash-cli.lock
191 | 139 silly lockFile 01c44be2-npm-lodash-cli-3-0-0-package-tgz tar:///home/gajus/.npm/lodash-cli/3.0.0/package.tgz
192 | 140 verbose lock tar:///home/gajus/.npm/lodash-cli/3.0.0/package.tgz /home/gajus/.npm/01c44be2-npm-lodash-cli-3-0-0-package-tgz.lock
193 | 141 silly gunzTarPerm modes [ '755', '644' ]
194 | 142 silly gunzTarPerm extractEntry package.json
195 | 143 silly gunzTarPerm extractEntry README.md
196 | 144 silly gunzTarPerm extractEntry LICENSE.txt
197 | 145 silly gunzTarPerm extractEntry bin/lodash
198 | 146 silly gunzTarPerm extractEntry lib/minify.js
199 | 147 silly gunzTarPerm extractEntry lib/post-compile.js
200 | 148 silly gunzTarPerm extractEntry lib/pre-compile.js
201 | 149 silly gunzTarPerm extractEntry lib/util.js
202 | 150 silly gunzTarPerm extractEntry template/license.jst
203 | 151 silly gunzTarPerm extractEntry template/package.jst
204 | 152 silly gunzTarPerm extractEntry template/readme.jst
205 | 153 silly lockFile 7e402026-enerator-node-modules-lodash-cli tar:///var/www/dev/gajus kuizinas/2014 02 02 gajus.com/_blog/post/lodash-v3/documentation-generator/node_modules/lodash-cli
206 | 154 silly lockFile 7e402026-enerator-node-modules-lodash-cli tar:///var/www/dev/gajus kuizinas/2014 02 02 gajus.com/_blog/post/lodash-v3/documentation-generator/node_modules/lodash-cli
207 | 155 silly lockFile 01c44be2-npm-lodash-cli-3-0-0-package-tgz tar:///home/gajus/.npm/lodash-cli/3.0.0/package.tgz
208 | 156 silly lockFile 01c44be2-npm-lodash-cli-3-0-0-package-tgz tar:///home/gajus/.npm/lodash-cli/3.0.0/package.tgz
209 | 157 info preinstall lodash-cli@3.0.0
210 | 158 verbose readDependencies using package.json deps
211 | 159 verbose readDependencies using package.json deps
212 | 160 verbose cache add [ 'closure-compiler@0.2.6', null ]
213 | 161 verbose cache add name=undefined spec="closure-compiler@0.2.6" args=["closure-compiler@0.2.6",null]
214 | 162 verbose parsed url { protocol: null,
215 | 162 verbose parsed url slashes: null,
216 | 162 verbose parsed url auth: null,
217 | 162 verbose parsed url host: null,
218 | 162 verbose parsed url port: null,
219 | 162 verbose parsed url hostname: null,
220 | 162 verbose parsed url hash: null,
221 | 162 verbose parsed url search: null,
222 | 162 verbose parsed url query: null,
223 | 162 verbose parsed url pathname: 'closure-compiler@0.2.6',
224 | 162 verbose parsed url path: 'closure-compiler@0.2.6',
225 | 162 verbose parsed url href: 'closure-compiler@0.2.6' }
226 | 163 verbose cache add name="closure-compiler" spec="0.2.6" args=["closure-compiler","0.2.6"]
227 | 164 verbose parsed url { protocol: null,
228 | 164 verbose parsed url slashes: null,
229 | 164 verbose parsed url auth: null,
230 | 164 verbose parsed url host: null,
231 | 164 verbose parsed url port: null,
232 | 164 verbose parsed url hostname: null,
233 | 164 verbose parsed url hash: null,
234 | 164 verbose parsed url search: null,
235 | 164 verbose parsed url query: null,
236 | 164 verbose parsed url pathname: '0.2.6',
237 | 164 verbose parsed url path: '0.2.6',
238 | 164 verbose parsed url href: '0.2.6' }
239 | 165 verbose addNamed [ 'closure-compiler', '0.2.6' ]
240 | 166 verbose addNamed [ '0.2.6', '0.2.6' ]
241 | 167 silly lockFile e0e0009e-closure-compiler-0-2-6 closure-compiler@0.2.6
242 | 168 verbose lock closure-compiler@0.2.6 /home/gajus/.npm/e0e0009e-closure-compiler-0-2-6.lock
243 | 169 verbose cache add [ 'glob@~4.3.5', null ]
244 | 170 verbose cache add name=undefined spec="glob@~4.3.5" args=["glob@~4.3.5",null]
245 | 171 verbose parsed url { protocol: null,
246 | 171 verbose parsed url slashes: null,
247 | 171 verbose parsed url auth: null,
248 | 171 verbose parsed url host: null,
249 | 171 verbose parsed url port: null,
250 | 171 verbose parsed url hostname: null,
251 | 171 verbose parsed url hash: null,
252 | 171 verbose parsed url search: null,
253 | 171 verbose parsed url query: null,
254 | 171 verbose parsed url pathname: 'glob@~4.3.5',
255 | 171 verbose parsed url path: 'glob@~4.3.5',
256 | 171 verbose parsed url href: 'glob@~4.3.5' }
257 | 172 verbose cache add name="glob" spec="~4.3.5" args=["glob","~4.3.5"]
258 | 173 verbose parsed url { protocol: null,
259 | 173 verbose parsed url slashes: null,
260 | 173 verbose parsed url auth: null,
261 | 173 verbose parsed url host: null,
262 | 173 verbose parsed url port: null,
263 | 173 verbose parsed url hostname: null,
264 | 173 verbose parsed url hash: null,
265 | 173 verbose parsed url search: null,
266 | 173 verbose parsed url query: null,
267 | 173 verbose parsed url pathname: '~4.3.5',
268 | 173 verbose parsed url path: '~4.3.5',
269 | 173 verbose parsed url href: '~4.3.5' }
270 | 174 verbose addNamed [ 'glob', '~4.3.5' ]
271 | 175 verbose addNamed [ null, '>=4.3.5-0 <4.4.0-0' ]
272 | 176 silly lockFile 85d3ad16-glob-4-3-5 glob@~4.3.5
273 | 177 verbose lock glob@~4.3.5 /home/gajus/.npm/85d3ad16-glob-4-3-5.lock
274 | 178 verbose cache add [ 'lodash-compat@3.0.0', null ]
275 | 179 verbose cache add name=undefined spec="lodash-compat@3.0.0" args=["lodash-compat@3.0.0",null]
276 | 180 verbose parsed url { protocol: null,
277 | 180 verbose parsed url slashes: null,
278 | 180 verbose parsed url auth: null,
279 | 180 verbose parsed url host: null,
280 | 180 verbose parsed url port: null,
281 | 180 verbose parsed url hostname: null,
282 | 180 verbose parsed url hash: null,
283 | 180 verbose parsed url search: null,
284 | 180 verbose parsed url query: null,
285 | 180 verbose parsed url pathname: 'lodash-compat@3.0.0',
286 | 180 verbose parsed url path: 'lodash-compat@3.0.0',
287 | 180 verbose parsed url href: 'lodash-compat@3.0.0' }
288 | 181 verbose cache add name="lodash-compat" spec="3.0.0" args=["lodash-compat","3.0.0"]
289 | 182 verbose parsed url { protocol: null,
290 | 182 verbose parsed url slashes: null,
291 | 182 verbose parsed url auth: null,
292 | 182 verbose parsed url host: null,
293 | 182 verbose parsed url port: null,
294 | 182 verbose parsed url hostname: null,
295 | 182 verbose parsed url hash: null,
296 | 182 verbose parsed url search: null,
297 | 182 verbose parsed url query: null,
298 | 182 verbose parsed url pathname: '3.0.0',
299 | 182 verbose parsed url path: '3.0.0',
300 | 182 verbose parsed url href: '3.0.0' }
301 | 183 verbose addNamed [ 'lodash-compat', '3.0.0' ]
302 | 184 verbose addNamed [ '3.0.0', '3.0.0' ]
303 | 185 silly lockFile 3c21c534-lodash-compat-3-0-0 lodash-compat@3.0.0
304 | 186 verbose lock lodash-compat@3.0.0 /home/gajus/.npm/3c21c534-lodash-compat-3-0-0.lock
305 | 187 verbose cache add [ 'uglify-js@2.4.16', null ]
306 | 188 verbose cache add name=undefined spec="uglify-js@2.4.16" args=["uglify-js@2.4.16",null]
307 | 189 verbose parsed url { protocol: null,
308 | 189 verbose parsed url slashes: null,
309 | 189 verbose parsed url auth: null,
310 | 189 verbose parsed url host: null,
311 | 189 verbose parsed url port: null,
312 | 189 verbose parsed url hostname: null,
313 | 189 verbose parsed url hash: null,
314 | 189 verbose parsed url search: null,
315 | 189 verbose parsed url query: null,
316 | 189 verbose parsed url pathname: 'uglify-js@2.4.16',
317 | 189 verbose parsed url path: 'uglify-js@2.4.16',
318 | 189 verbose parsed url href: 'uglify-js@2.4.16' }
319 | 190 verbose cache add name="uglify-js" spec="2.4.16" args=["uglify-js","2.4.16"]
320 | 191 verbose parsed url { protocol: null,
321 | 191 verbose parsed url slashes: null,
322 | 191 verbose parsed url auth: null,
323 | 191 verbose parsed url host: null,
324 | 191 verbose parsed url port: null,
325 | 191 verbose parsed url hostname: null,
326 | 191 verbose parsed url hash: null,
327 | 191 verbose parsed url search: null,
328 | 191 verbose parsed url query: null,
329 | 191 verbose parsed url pathname: '2.4.16',
330 | 191 verbose parsed url path: '2.4.16',
331 | 191 verbose parsed url href: '2.4.16' }
332 | 192 verbose addNamed [ 'uglify-js', '2.4.16' ]
333 | 193 verbose addNamed [ '2.4.16', '2.4.16' ]
334 | 194 silly lockFile 20fcf559-uglify-js-2-4-16 uglify-js@2.4.16
335 | 195 verbose lock uglify-js@2.4.16 /home/gajus/.npm/20fcf559-uglify-js-2-4-16.lock
336 | 196 silly addNameRange { name: 'glob', range: '>=4.3.5-0 <4.4.0-0', hasData: false }
337 | 197 verbose url raw lodash-compat/3.0.0
338 | 198 verbose url resolving [ 'https://registry.npmjs.org/', './lodash-compat/3.0.0' ]
339 | 199 verbose url resolved https://registry.npmjs.org/lodash-compat/3.0.0
340 | 200 info trying registry request attempt 1 at 14:51:04
341 | 201 http GET https://registry.npmjs.org/lodash-compat/3.0.0
342 | 202 verbose url raw closure-compiler/0.2.6
343 | 203 verbose url resolving [ 'https://registry.npmjs.org/', './closure-compiler/0.2.6' ]
344 | 204 verbose url resolved https://registry.npmjs.org/closure-compiler/0.2.6
345 | 205 info trying registry request attempt 1 at 14:51:04
346 | 206 verbose etag "I3D7KGEM7LAZWJWSOCFGJJSS"
347 | 207 http GET https://registry.npmjs.org/closure-compiler/0.2.6
348 | 208 verbose url raw glob
349 | 209 verbose url resolving [ 'https://registry.npmjs.org/', './glob' ]
350 | 210 verbose url resolved https://registry.npmjs.org/glob
351 | 211 info trying registry request attempt 1 at 14:51:04
352 | 212 verbose etag "SRRWGGPL6V94MIR6QKPMBZHL"
353 | 213 http GET https://registry.npmjs.org/glob
354 | 214 verbose url raw uglify-js/2.4.16
355 | 215 verbose url resolving [ 'https://registry.npmjs.org/', './uglify-js/2.4.16' ]
356 | 216 verbose url resolved https://registry.npmjs.org/uglify-js/2.4.16
357 | 217 info trying registry request attempt 1 at 14:51:04
358 | 218 verbose etag "15GXROFMIO0K0BMM3T3RH5IS3"
359 | 219 http GET https://registry.npmjs.org/uglify-js/2.4.16
360 | 220 http 304 https://registry.npmjs.org/glob
361 | 221 silly registry.get cb [ 304,
362 | 221 silly registry.get { date: 'Tue, 20 Jan 2015 14:51:04 GMT',
363 | 221 silly registry.get server: 'Apache',
364 | 221 silly registry.get via: '1.1 varnish',
365 | 221 silly registry.get 'last-modified': 'Tue, 20 Jan 2015 14:51:04 GMT',
366 | 221 silly registry.get 'cache-control': 'max-age=60',
367 | 221 silly registry.get etag: '"SRRWGGPL6V94MIR6QKPMBZHL"',
368 | 221 silly registry.get age: '2',
369 | 221 silly registry.get 'x-served-by': 'cache-ams4123-AMS',
370 | 221 silly registry.get 'x-cache': 'HIT',
371 | 221 silly registry.get 'x-cache-hits': '1',
372 | 221 silly registry.get 'x-timer': 'S1421765464.054062,VS0,VE0',
373 | 221 silly registry.get vary: 'Accept',
374 | 221 silly registry.get 'content-length': '0',
375 | 221 silly registry.get 'keep-alive': 'timeout=10, max=50',
376 | 221 silly registry.get connection: 'Keep-Alive' } ]
377 | 222 verbose etag glob from cache
378 | 223 silly addNameRange number 2 { name: 'glob', range: '>=4.3.5-0 <4.4.0-0', hasData: true }
379 | 224 silly addNameRange versions [ 'glob',
380 | 224 silly addNameRange [ '1.1.0',
381 | 224 silly addNameRange '2.0.9',
382 | 224 silly addNameRange '2.0.8',
383 | 224 silly addNameRange '2.0.7',
384 | 224 silly addNameRange '2.1.0',
385 | 224 silly addNameRange '3.0.0',
386 | 224 silly addNameRange '3.0.1',
387 | 224 silly addNameRange '3.1.0',
388 | 224 silly addNameRange '3.1.1',
389 | 224 silly addNameRange '3.1.2',
390 | 224 silly addNameRange '3.1.3',
391 | 224 silly addNameRange '3.1.4',
392 | 224 silly addNameRange '3.1.5',
393 | 224 silly addNameRange '3.1.6',
394 | 224 silly addNameRange '3.1.7',
395 | 224 silly addNameRange '3.1.9',
396 | 224 silly addNameRange '3.1.10',
397 | 224 silly addNameRange '3.1.11',
398 | 224 silly addNameRange '3.1.12',
399 | 224 silly addNameRange '3.1.13',
400 | 224 silly addNameRange '3.1.14',
401 | 224 silly addNameRange '3.1.15',
402 | 224 silly addNameRange '3.1.16',
403 | 224 silly addNameRange '3.1.17',
404 | 224 silly addNameRange '3.1.18',
405 | 224 silly addNameRange '3.1.19',
406 | 224 silly addNameRange '3.1.20',
407 | 224 silly addNameRange '3.1.21',
408 | 224 silly addNameRange '3.2.0',
409 | 224 silly addNameRange '3.2.1',
410 | 224 silly addNameRange '3.2.3',
411 | 224 silly addNameRange '3.2.4',
412 | 224 silly addNameRange '3.2.5',
413 | 224 silly addNameRange '3.2.6',
414 | 224 silly addNameRange '3.2.7',
415 | 224 silly addNameRange '3.2.8',
416 | 224 silly addNameRange '3.2.9',
417 | 224 silly addNameRange '3.2.10',
418 | 224 silly addNameRange '3.2.11',
419 | 224 silly addNameRange '4.0.0',
420 | 224 silly addNameRange '4.0.1',
421 | 224 silly addNameRange '4.0.2',
422 | 224 silly addNameRange '4.0.3',
423 | 224 silly addNameRange '4.0.4',
424 | 224 silly addNameRange '4.0.5',
425 | 224 silly addNameRange '4.0.6',
426 | 224 silly addNameRange '4.1.2-beta',
427 | 224 silly addNameRange '4.1.2',
428 | 224 silly addNameRange '4.1.3',
429 | 224 silly addNameRange '4.1.4',
430 | 224 silly addNameRange '4.1.5',
431 | 224 silly addNameRange '4.1.6',
432 | 224 silly addNameRange '4.2.0',
433 | 224 silly addNameRange '4.2.1',
434 | 224 silly addNameRange '4.2.2',
435 | 224 silly addNameRange '4.3.0',
436 | 224 silly addNameRange '4.3.1',
437 | 224 silly addNameRange '4.3.2',
438 | 224 silly addNameRange '4.3.3',
439 | 224 silly addNameRange '4.3.4',
440 | 224 silly addNameRange '4.3.5' ] ]
441 | 225 verbose addNamed [ 'glob', '4.3.5' ]
442 | 226 verbose addNamed [ '4.3.5', '4.3.5' ]
443 | 227 silly lockFile 88cb2229-glob-4-3-5 glob@4.3.5
444 | 228 verbose lock glob@4.3.5 /home/gajus/.npm/88cb2229-glob-4-3-5.lock
445 | 229 silly lockFile 88cb2229-glob-4-3-5 glob@4.3.5
446 | 230 silly lockFile 88cb2229-glob-4-3-5 glob@4.3.5
447 | 231 silly lockFile 85d3ad16-glob-4-3-5 glob@~4.3.5
448 | 232 silly lockFile 85d3ad16-glob-4-3-5 glob@~4.3.5
449 | 233 http 404 https://registry.npmjs.org/lodash-compat/3.0.0
450 | 234 silly registry.get cb [ 404,
451 | 234 silly registry.get { date: 'Tue, 20 Jan 2015 14:51:04 GMT',
452 | 234 silly registry.get server: 'CouchDB/1.5.0 (Erlang OTP/R16B03)',
453 | 234 silly registry.get 'content-type': 'application/json',
454 | 234 silly registry.get 'cache-control': 'max-age=0',
455 | 234 silly registry.get 'content-length': '52',
456 | 234 silly registry.get 'accept-ranges': 'bytes',
457 | 234 silly registry.get via: '1.1 varnish',
458 | 234 silly registry.get age: '0',
459 | 234 silly registry.get 'x-served-by': 'cache-ams4134-AMS',
460 | 234 silly registry.get 'x-cache': 'MISS',
461 | 234 silly registry.get 'x-cache-hits': '0',
462 | 234 silly registry.get 'x-timer': 'S1421765464.064276,VS0,VE90',
463 | 234 silly registry.get 'keep-alive': 'timeout=10, max=50',
464 | 234 silly registry.get connection: 'Keep-Alive' } ]
465 | 235 silly lockFile 3c21c534-lodash-compat-3-0-0 lodash-compat@3.0.0
466 | 236 silly lockFile 3c21c534-lodash-compat-3-0-0 lodash-compat@3.0.0
467 | 237 verbose about to build /var/www/dev/gajus kuizinas/2014 02 02 gajus.com/_blog/post/lodash-v3/documentation-generator/node_modules/lodash-cli
468 | 238 info /var/www/dev/gajus kuizinas/2014 02 02 gajus.com/_blog/post/lodash-v3/documentation-generator/node_modules/lodash-cli unbuild
469 | 239 info preuninstall lodash-cli@3.0.0
470 | 240 info uninstall lodash-cli@3.0.0
471 | 241 verbose true,/var/www/dev/gajus kuizinas/2014 02 02 gajus.com/_blog/post/lodash-v3/documentation-generator/node_modules,/var/www/dev/gajus kuizinas/2014 02 02 gajus.com/_blog/post/lodash-v3/documentation-generator/node_modules unbuild lodash-cli@3.0.0
472 | 242 verbose /var/www/dev/gajus kuizinas/2014 02 02 gajus.com/_blog/post/lodash-v3/documentation-generator/node_modules/.bin,[object Object] binRoot
473 | 243 info postuninstall lodash-cli@3.0.0
474 | 244 error 404 'lodash-compat' is not in the npm registry.
475 | 244 error 404 You should bug the author to publish it
476 | 244 error 404
477 | 244 error 404 Note that you can also install from a
478 | 244 error 404 tarball, folder, or http url, or git url.
479 | 245 error System Linux 2.6.32-431.11.2.el6.x86_64
480 | 246 error command "node" "/usr/bin/npm" "install" "git+https://github.com/lodash/lodash-cli.git"
481 | 247 error cwd /var/www/dev/gajus kuizinas/2014 02 02 gajus.com/_blog/post/lodash-v3/documentation-generator
482 | 248 error node -v v0.10.26
483 | 249 error npm -v 1.3.6
484 | 250 error code E404
485 | 251 verbose exit [ 1, true ]
486 |
--------------------------------------------------------------------------------
/posts/harder-better-faster-stronger-lo-dash-v3/documentation-generator/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "documentation-generator",
3 | "description": "Generates documentation for Lo-Dash v3.",
4 | "dependencies": {
5 | "request-promise": "~0.3.3",
6 | "bluebird": "~2.8.1",
7 | "comment-parser": "~0.2.4",
8 | "lodash": "~2.4.1"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/posts/harder-better-faster-stronger-lo-dash-v3/documentation.md:
--------------------------------------------------------------------------------
1 | # Lo-Dash v3 Documentation
2 |
3 | Lo-Dash v3 documentation generated from the source code.
4 |
5 | ## string
6 |
7 | ### camelCase
8 |
9 | https://raw.githubusercontent.com/lodash/lodash/es6/string/camelCase.js
10 |
11 | Converts `string` to camel case.
12 |
13 | See [Wikipedia](http://en.wikipedia.org/wiki/CamelCase) for more details.
14 |
15 | #### Parameters
16 |
17 | | Name | Type | Description |
18 | | --- | --- | --- |
19 | | `string` | `string` | The string to convert.|
20 |
21 |
22 | #### Returns
23 |
24 | | Type | Description |
25 | | --- | --- |
26 | | `string` | the camel cased string.|
27 |
28 | ```js
29 | _.camelCase('Foo Bar');
30 | // => 'fooBar'
31 |
32 | _.camelCase('--foo-bar');
33 | // => 'fooBar'
34 |
35 | _.camelCase('__foo_bar__');
36 | // => 'fooBar'
37 | ```
38 |
39 | ### capitalize
40 |
41 | https://raw.githubusercontent.com/lodash/lodash/es6/string/capitalize.js
42 |
43 | Capitalizes the first character of `string`.
44 |
45 | #### Parameters
46 |
47 | | Name | Type | Description |
48 | | --- | --- | --- |
49 | | `string` | `string` | The string to capitalize.|
50 |
51 |
52 | #### Returns
53 |
54 | | Type | Description |
55 | | --- | --- |
56 | | `string` | the capitalized string.|
57 |
58 | ```js
59 | _.capitalize('fred');
60 | // => 'Fred'
61 | ```
62 |
63 | ### deburr
64 |
65 | https://raw.githubusercontent.com/lodash/lodash/es6/string/deburr.js
66 |
67 | Deburrs `string` by converting latin-1 supplementary letters to basic latin letters.
68 |
69 | See [Wikipedia](http://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table)
70 |
71 | for more details.
72 |
73 | #### Parameters
74 |
75 | | Name | Type | Description |
76 | | --- | --- | --- |
77 | | `string` | `string` | The string to deburr.|
78 |
79 |
80 | #### Returns
81 |
82 | | Type | Description |
83 | | --- | --- |
84 | | `string` | the deburred string.|
85 |
86 | ```js
87 | _.deburr('déjà vu');
88 | // => 'deja vu'
89 | ```
90 |
91 | ### endsWith
92 |
93 | https://raw.githubusercontent.com/lodash/lodash/es6/string/endsWith.js
94 |
95 | Checks if `string` ends with the given target string.
96 |
97 | #### Parameters
98 |
99 | | Name | Type | Description |
100 | | --- | --- | --- |
101 | | `string` | `string` | The string to search.|
102 | | `target` | `string` | The string to search for.|
103 | | `position` | `number` | The position to search from.|
104 |
105 |
106 | #### Returns
107 |
108 | | Type | Description |
109 | | --- | --- |
110 | | `boolean` | `true` if `string` ends with `target`, else `false`.|
111 |
112 | ```js
113 | _.endsWith('abc', 'c');
114 | // => true
115 |
116 | _.endsWith('abc', 'b');
117 | // => false
118 |
119 | _.endsWith('abc', 'b', 2);
120 | // => true
121 | ```
122 |
123 | ### escapeRegExp
124 |
125 | https://raw.githubusercontent.com/lodash/lodash/es6/string/escapeRegExp.js
126 |
127 | Escapes the `RegExp` special characters "\", "^", "$", ".", "|", "?", "*",
128 |
129 | "+", "(", ")", "[", "]", "{" and "}" in `string`.
130 |
131 | #### Parameters
132 |
133 | | Name | Type | Description |
134 | | --- | --- | --- |
135 | | `string` | `string` | The string to escape.|
136 |
137 |
138 | #### Returns
139 |
140 | | Type | Description |
141 | | --- | --- |
142 | | `string` | the escaped string.|
143 |
144 | ```js
145 | _.escapeRegExp('[lodash](https://lodash.com/)');
146 | // => '\[lodash\]\(https://lodash\.com/\)'
147 | ```
148 |
149 | ### kebabCase
150 |
151 | https://raw.githubusercontent.com/lodash/lodash/es6/string/kebabCase.js
152 |
153 | Converts `string` to kebab case (a.k.a. spinal case).
154 |
155 | See [Wikipedia](http://en.wikipedia.org/wiki/Letter_case#Computers) for
156 |
157 | more details.
158 |
159 | #### Parameters
160 |
161 | | Name | Type | Description |
162 | | --- | --- | --- |
163 | | `string` | `string` | The string to convert.|
164 |
165 |
166 | #### Returns
167 |
168 | | Type | Description |
169 | | --- | --- |
170 | | `string` | the kebab cased string.|
171 |
172 | ```js
173 | _.kebabCase('Foo Bar');
174 | // => 'foo-bar'
175 |
176 | _.kebabCase('fooBar');
177 | // => 'foo-bar'
178 |
179 | _.kebabCase('__foo_bar__');
180 | // => 'foo-bar'
181 | ```
182 |
183 | ### pad
184 |
185 | https://raw.githubusercontent.com/lodash/lodash/es6/string/pad.js
186 |
187 | Pads `string` on the left and right sides if it is shorter then the given
188 |
189 | padding length. The `chars` string may be truncated if the number of padding
190 |
191 | characters can't be evenly divided by the padding length.
192 |
193 | #### Parameters
194 |
195 | | Name | Type | Description |
196 | | --- | --- | --- |
197 | | `string` | `string` | The string to pad.|
198 | | `length` | `number` | The padding length.|
199 | | `chars` | `string` | The string used as padding.|
200 |
201 |
202 | #### Returns
203 |
204 | | Type | Description |
205 | | --- | --- |
206 | | `string` | the padded string.|
207 |
208 | ```js
209 | _.pad('abc', 8);
210 | // => ' abc '
211 |
212 | _.pad('abc', 8, '_-');
213 | // => '_-abc_-_'
214 |
215 | _.pad('abc', 3);
216 | // => 'abc'
217 | ```
218 |
219 | ### padLeft
220 |
221 | https://raw.githubusercontent.com/lodash/lodash/es6/string/padLeft.js
222 |
223 | Pads `string` on the left side if it is shorter then the given padding
224 |
225 | length. The `chars` string may be truncated if the number of padding
226 |
227 | characters exceeds the padding length.
228 |
229 | #### Parameters
230 |
231 | | Name | Type | Description |
232 | | --- | --- | --- |
233 | | `string` | `string` | The string to pad.|
234 | | `length` | `number` | The padding length.|
235 | | `chars` | `string` | The string used as padding.|
236 |
237 |
238 | #### Returns
239 |
240 | | Type | Description |
241 | | --- | --- |
242 | | `string` | the padded string.|
243 |
244 | ```js
245 | _.padLeft('abc', 6);
246 | // => ' abc'
247 |
248 | _.padLeft('abc', 6, '_-');
249 | // => '_-_abc'
250 |
251 | _.padLeft('abc', 3);
252 | // => 'abc'
253 | ```
254 |
255 | ### padRight
256 |
257 | https://raw.githubusercontent.com/lodash/lodash/es6/string/padRight.js
258 |
259 | Pads `string` on the right side if it is shorter then the given padding
260 |
261 | length. The `chars` string may be truncated if the number of padding
262 |
263 | characters exceeds the padding length.
264 |
265 | #### Parameters
266 |
267 | | Name | Type | Description |
268 | | --- | --- | --- |
269 | | `string` | `string` | The string to pad.|
270 | | `length` | `number` | The padding length.|
271 | | `chars` | `string` | The string used as padding.|
272 |
273 |
274 | #### Returns
275 |
276 | | Type | Description |
277 | | --- | --- |
278 | | `string` | the padded string.|
279 |
280 | ```js
281 | _.padRight('abc', 6);
282 | // => 'abc '
283 |
284 | _.padRight('abc', 6, '_-');
285 | // => 'abc_-_'
286 |
287 | _.padRight('abc', 3);
288 | // => 'abc'
289 | ```
290 |
291 | ### repeat
292 |
293 | https://raw.githubusercontent.com/lodash/lodash/es6/string/repeat.js
294 |
295 | Repeats the given string `n` times.
296 |
297 | #### Parameters
298 |
299 | | Name | Type | Description |
300 | | --- | --- | --- |
301 | | `string` | `string` | The string to repeat.|
302 | | `n` | `number` | The number of times to repeat the string.|
303 |
304 |
305 | #### Returns
306 |
307 | | Type | Description |
308 | | --- | --- |
309 | | `string` | the repeated string.|
310 |
311 | ```js
312 | _.repeat('*', 3);
313 | // => '***'
314 |
315 | _.repeat('abc', 2);
316 | // => 'abcabc'
317 |
318 | _.repeat('abc', 0);
319 | // => ''
320 | ```
321 |
322 | ### snakeCase
323 |
324 | https://raw.githubusercontent.com/lodash/lodash/es6/string/snakeCase.js
325 |
326 | Converts `string` to snake case.
327 |
328 | See [Wikipedia](http://en.wikipedia.org/wiki/Snake_case) for more details.
329 |
330 | #### Parameters
331 |
332 | | Name | Type | Description |
333 | | --- | --- | --- |
334 | | `string` | `string` | The string to convert.|
335 |
336 |
337 | #### Returns
338 |
339 | | Type | Description |
340 | | --- | --- |
341 | | `string` | the snake cased string.|
342 |
343 | ```js
344 | _.snakeCase('Foo Bar');
345 | // => 'foo_bar'
346 |
347 | _.snakeCase('--foo-bar');
348 | // => 'foo_bar'
349 |
350 | _.snakeCase('fooBar');
351 | // => 'foo_bar'
352 | ```
353 |
354 | ### startsWith
355 |
356 | https://raw.githubusercontent.com/lodash/lodash/es6/string/startsWith.js
357 |
358 | Checks if `string` starts with the given target string.
359 |
360 | #### Parameters
361 |
362 | | Name | Type | Description |
363 | | --- | --- | --- |
364 | | `string` | `string` | The string to search.|
365 | | `target` | `string` | The string to search for.|
366 | | `position` | `number` | The position to search from.|
367 |
368 |
369 | #### Returns
370 |
371 | | Type | Description |
372 | | --- | --- |
373 | | `boolean` | `true` if `string` starts with `target`, else `false`.|
374 |
375 | ```js
376 | _.startsWith('abc', 'a');
377 | // => true
378 |
379 | _.startsWith('abc', 'b');
380 | // => false
381 |
382 | _.startsWith('abc', 'b', 1);
383 | // => true
384 | ```
385 |
386 | ### trim
387 |
388 | https://raw.githubusercontent.com/lodash/lodash/es6/string/trim.js
389 |
390 | Removes leading and trailing whitespace or specified characters from `string`.
391 |
392 | #### Parameters
393 |
394 | | Name | Type | Description |
395 | | --- | --- | --- |
396 | | `string` | `string` | The string to trim.|
397 | | `chars` | `string` | The characters to trim.|
398 |
399 |
400 | #### Returns
401 |
402 | | Type | Description |
403 | | --- | --- |
404 | | `string` | the trimmed string.|
405 |
406 | ```js
407 | _.trim(' abc ');
408 | // => 'abc'
409 |
410 | _.trim('-_-abc-_-', '_-');
411 | // => 'abc'
412 |
413 | _.map([' foo ', ' bar '], _.trim);
414 | // => ['foo', 'bar]
415 | ```
416 |
417 | ### trimLeft
418 |
419 | https://raw.githubusercontent.com/lodash/lodash/es6/string/trimLeft.js
420 |
421 | Removes leading whitespace or specified characters from `string`.
422 |
423 | #### Parameters
424 |
425 | | Name | Type | Description |
426 | | --- | --- | --- |
427 | | `string` | `string` | The string to trim.|
428 | | `chars` | `string` | The characters to trim.|
429 |
430 |
431 | #### Returns
432 |
433 | | Type | Description |
434 | | --- | --- |
435 | | `string` | the trimmed string.|
436 |
437 | ```js
438 | _.trimLeft(' abc ');
439 | // => 'abc '
440 |
441 | _.trimLeft('-_-abc-_-', '_-');
442 | // => 'abc-_-'
443 | ```
444 |
445 | ### trimRight
446 |
447 | https://raw.githubusercontent.com/lodash/lodash/es6/string/trimRight.js
448 |
449 | Removes trailing whitespace or specified characters from `string`.
450 |
451 | #### Parameters
452 |
453 | | Name | Type | Description |
454 | | --- | --- | --- |
455 | | `string` | `string` | The string to trim.|
456 | | `chars` | `string` | The characters to trim.|
457 |
458 |
459 | #### Returns
460 |
461 | | Type | Description |
462 | | --- | --- |
463 | | `string` | the trimmed string.|
464 |
465 | ```js
466 | _.trimRight(' abc ');
467 | // => ' abc'
468 |
469 | _.trimRight('-_-abc-_-', '_-');
470 | // => '-_-abc'
471 | ```
472 |
473 | ### trunc
474 |
475 | https://raw.githubusercontent.com/lodash/lodash/es6/string/trunc.js
476 |
477 | Truncates `string` if it is longer than the given maximum string length.
478 |
479 | The last characters of the truncated string are replaced with the omission
480 |
481 | string which defaults to "...".
482 |
483 | #### Parameters
484 |
485 | | Name | Type | Description |
486 | | --- | --- | --- |
487 | | `string` | `string` | The string to truncate.|
488 | | `options` | `Object|number` | The options object or maximum string length.|
489 | | `options.length` | `number` | The maximum string length.|
490 | | `options.omission` | `string` | The string to indicate text is omitted.|
491 | | `options.separator` | `RegExp|string` | The separator pattern to truncate to.|
492 |
493 |
494 | #### Returns
495 |
496 | | Type | Description |
497 | | --- | --- |
498 | | `string` | the truncated string.|
499 |
500 | ```js
501 | _.trunc('hi-diddly-ho there, neighborino');
502 | // => 'hi-diddly-ho there, neighbo...'
503 |
504 | _.trunc('hi-diddly-ho there, neighborino', 24);
505 | // => 'hi-diddly-ho there, n...'
506 |
507 | _.trunc('hi-diddly-ho there, neighborino', { 'length': 24, 'separator': ' ' });
508 | // => 'hi-diddly-ho there,...'
509 |
510 | _.trunc('hi-diddly-ho there, neighborino', { 'length': 24, 'separator': /,? +/ });
511 | //=> 'hi-diddly-ho there...'
512 |
513 | _.trunc('hi-diddly-ho there, neighborino', { 'omission': ' [...]' });
514 | // => 'hi-diddly-ho there, neig [...]'
515 | ```
516 |
517 | ### words
518 |
519 | https://raw.githubusercontent.com/lodash/lodash/es6/string/words.js
520 |
521 | Splits `string` into an array of its words.
522 |
523 | #### Parameters
524 |
525 | | Name | Type | Description |
526 | | --- | --- | --- |
527 | | `string` | `string` | The string to inspect.|
528 | | `pattern` | `RegExp|string` | The pattern to match words.|
529 |
530 |
531 | #### Returns
532 |
533 | | Type | Description |
534 | | --- | --- |
535 | | `Array` | the words of `string`.|
536 |
537 | ```js
538 | _.words('fred, barney, & pebbles');
539 | // => ['fred', 'barney', 'pebbles']
540 |
541 | _.words('fred, barney, & pebbles', /[^, ]+/g);
542 | // => ['fred', 'barney', '&', 'pebbles']
543 | ```
544 |
545 | ## array
546 |
547 | ### chunk
548 |
549 | https://raw.githubusercontent.com/lodash/lodash/es6/array/chunk.js
550 |
551 | Creates an array of elements split into groups the length of `size`.
552 |
553 | If `collection` can't be split evenly, the final chunk will be the remaining
554 |
555 | elements.
556 |
557 | #### Parameters
558 |
559 | | Name | Type | Description |
560 | | --- | --- | --- |
561 | | `array` | `Array` | The array to process.|
562 | | `size` | `numer` | The length of each chunk.|
563 |
564 |
565 | #### Returns
566 |
567 | | Type | Description |
568 | | --- | --- |
569 | | `Array` | the new array containing chunks.|
570 |
571 | ```js
572 | _.chunk(['a', 'b', 'c', 'd'], 2);
573 | // => [['a', 'b'], ['c', 'd']]
574 |
575 | _.chunk(['a', 'b', 'c', 'd'], 3);
576 | // => [['a', 'b', 'c'], ['d']]
577 | ```
578 |
579 | ### dropRight
580 |
581 | https://raw.githubusercontent.com/lodash/lodash/es6/array/dropRight.js
582 |
583 | Creates a slice of `array` with `n` elements dropped from the end.
584 |
585 | #### Parameters
586 |
587 | | Name | Type | Description |
588 | | --- | --- | --- |
589 | | `array` | `Array` | The array to query.|
590 | | `n` | `number` | The number of elements to drop.|
591 |
592 |
593 | #### Returns
594 |
595 | | Type | Description |
596 | | --- | --- |
597 | | `Array` | the slice of `array`.|
598 |
599 | ```js
600 | _.dropRight([1, 2, 3]);
601 | // => [1, 2]
602 |
603 | _.dropRight([1, 2, 3], 2);
604 | // => [1]
605 |
606 | _.dropRight([1, 2, 3], 5);
607 | // => []
608 |
609 | _.dropRight([1, 2, 3], 0);
610 | // => [1, 2, 3]
611 | ```
612 |
613 | ### dropRightWhile
614 |
615 | https://raw.githubusercontent.com/lodash/lodash/es6/array/dropRightWhile.js
616 |
617 | Creates a slice of `array` excluding elements dropped from the end.
618 |
619 | Elements are dropped until `predicate` returns falsey. The predicate is
620 |
621 | bound to `thisArg` and invoked with three arguments; (value, index, array).
622 |
623 |
624 |
625 | If a property name is provided for `predicate` the created "_.property"
626 |
627 | style callback returns the property value of the given element.
628 |
629 |
630 |
631 | If an object is provided for `predicate` the created "_.matches" style
632 |
633 | callback returns `true` for elements that have the properties of the given
634 |
635 | object, else `false`.
636 |
637 | #### Parameters
638 |
639 | | Name | Type | Description |
640 | | --- | --- | --- |
641 | | `array` | `Array` | The array to query.|
642 | | `predicate` | `Function|Object|string` | The function invoked
643 | per element.|
644 | | `thisArg` | `*` | The `this` binding of `predicate`.|
645 |
646 |
647 | #### Returns
648 |
649 | | Type | Description |
650 | | --- | --- |
651 | | `Array` | the slice of `array`.|
652 |
653 | ```js
654 | _.dropRightWhile([1, 2, 3], function(n) { return n > 1; });
655 | // => [1]
656 |
657 | var users = [
658 | { 'user': 'barney', 'status': 'busy', 'active': false },
659 | { 'user': 'fred', 'status': 'busy', 'active': true },
660 | { 'user': 'pebbles', 'status': 'away', 'active': true }
661 | ];
662 |
663 | // using the "_.property" callback shorthand
664 | _.pluck(_.dropRightWhile(users, 'active'), 'user');
665 | // => ['barney']
666 |
667 | // using the "_.matches" callback shorthand
668 | _.pluck(_.dropRightWhile(users, { 'status': 'away' }), 'user');
669 | // => ['barney', 'fred']
670 | ```
671 |
672 | ### dropWhile
673 |
674 | https://raw.githubusercontent.com/lodash/lodash/es6/array/dropWhile.js
675 |
676 | Creates a slice of `array` excluding elements dropped from the beginning.
677 |
678 | Elements are dropped until `predicate` returns falsey. The predicate is
679 |
680 | bound to `thisArg` and invoked with three arguments; (value, index, array).
681 |
682 |
683 |
684 | If a property name is provided for `predicate` the created "_.property"
685 |
686 | style callback returns the property value of the given element.
687 |
688 |
689 |
690 | If an object is provided for `predicate` the created "_.matches" style
691 |
692 | callback returns `true` for elements that have the properties of the given
693 |
694 | object, else `false`.
695 |
696 | #### Parameters
697 |
698 | | Name | Type | Description |
699 | | --- | --- | --- |
700 | | `array` | `Array` | The array to query.|
701 | | `predicate` | `Function|Object|string` | The function invoked
702 | per element.|
703 | | `thisArg` | `*` | The `this` binding of `predicate`.|
704 |
705 |
706 | #### Returns
707 |
708 | | Type | Description |
709 | | --- | --- |
710 | | `Array` | the slice of `array`.|
711 |
712 | ```js
713 | _.dropWhile([1, 2, 3], function(n) { return n < 3; });
714 | // => [3]
715 |
716 | var users = [
717 | { 'user': 'barney', 'status': 'busy', 'active': true },
718 | { 'user': 'fred', 'status': 'busy', 'active': false },
719 | { 'user': 'pebbles', 'status': 'away', 'active': true }
720 | ];
721 |
722 | // using the "_.property" callback shorthand
723 | _.pluck(_.dropWhile(users, 'active'), 'user');
724 | // => ['fred', 'pebbles']
725 |
726 | // using the "_.matches" callback shorthand
727 | _.pluck(_.dropWhile(users, { 'status': 'busy' }), 'user');
728 | // => ['pebbles']
729 | ```
730 |
731 | ### flattenDeep
732 |
733 | https://raw.githubusercontent.com/lodash/lodash/es6/array/flattenDeep.js
734 |
735 | Recursively flattens a nested array.
736 |
737 | #### Parameters
738 |
739 | | Name | Type | Description |
740 | | --- | --- | --- |
741 | | `array` | `Array` | The array to recursively flatten.|
742 |
743 |
744 | #### Returns
745 |
746 | | Type | Description |
747 | | --- | --- |
748 | | `Array` | the new flattened array.|
749 |
750 | ```js
751 | _.flattenDeep([1, [2], [3, [[4]]]]);
752 | // => [1, 2, 3, 4];
753 | ```
754 |
755 | ### pullAt
756 |
757 | https://raw.githubusercontent.com/lodash/lodash/es6/array/pullAt.js
758 |
759 | Removes elements from `array` corresponding to the given indexes and returns
760 |
761 | an array of the removed elements. Indexes may be specified as an array of
762 |
763 | indexes or as individual arguments.
764 |
765 |
766 |
767 | **Note:** Unlike `_.at`, this method mutates `array`.
768 |
769 | #### Parameters
770 |
771 | | Name | Type | Description |
772 | | --- | --- | --- |
773 | | `array` | `Array` | The array to modify.|
774 | | `indexes` | `...(number|number[])` | The indexes of elements to remove,
775 | specified as individual indexes or arrays of indexes.|
776 |
777 |
778 | #### Returns
779 |
780 | | Type | Description |
781 | | --- | --- |
782 | | `Array` | the new array of removed elements.|
783 |
784 | ```js
785 | var array = [5, 10, 15, 20];
786 | var evens = _.pullAt(array, [1, 3]);
787 |
788 | console.log(array);
789 | // => [5, 15]
790 |
791 | console.log(evens);
792 | // => [10, 20]
793 | ```
794 |
795 | ### slice
796 |
797 | https://raw.githubusercontent.com/lodash/lodash/es6/array/slice.js
798 |
799 | Creates a slice of `array` from `start` up to, but not including, `end`.
800 |
801 |
802 |
803 | **Note:** This function is used instead of `Array#slice` to support node
804 |
805 | lists in IE < 9 and to ensure dense arrays are returned.
806 |
807 | #### Parameters
808 |
809 | | Name | Type | Description |
810 | | --- | --- | --- |
811 | | `array` | `Array` | The array to slice.|
812 | | `start` | `number` | The start position.|
813 | | `end` | `number` | The end position.|
814 |
815 |
816 | #### Returns
817 |
818 | | Type | Description |
819 | | --- | --- |
820 | | `Array` | the slice of `array`.|
821 |
822 |
823 | ### sortedLastIndex
824 |
825 | https://raw.githubusercontent.com/lodash/lodash/es6/array/sortedLastIndex.js
826 |
827 | This method is like `_.sortedIndex` except that it returns the highest
828 |
829 | index at which `value` should be inserted into `array` in order to
830 |
831 | maintain its sort order.
832 |
833 | #### Parameters
834 |
835 | | Name | Type | Description |
836 | | --- | --- | --- |
837 | | `array` | `Array` | The sorted array to inspect.|
838 | | `value` | `*` | The value to evaluate.|
839 | | `iteratee` | `Function|Object|string` | The function invoked
840 | per iteration. If a property name or object is provided it is used to
841 | create a "_.property" or "_.matches" style callback respectively.|
842 | | `thisArg` | `*` | The `this` binding of `iteratee`.|
843 |
844 |
845 | #### Returns
846 |
847 | | Type | Description |
848 | | --- | --- |
849 | | `number` | the index at which `value` should be inserted
850 | into `array`.|
851 |
852 | ```js
853 | _.sortedLastIndex([4, 4, 5, 5, 6, 6], 5);
854 | // => 4
855 | ```
856 |
857 | ### takeRight
858 |
859 | https://raw.githubusercontent.com/lodash/lodash/es6/array/takeRight.js
860 |
861 | Creates a slice of `array` with `n` elements taken from the end.
862 |
863 | #### Parameters
864 |
865 | | Name | Type | Description |
866 | | --- | --- | --- |
867 | | `array` | `Array` | The array to query.|
868 | | `n` | `number` | The number of elements to take.|
869 |
870 |
871 | #### Returns
872 |
873 | | Type | Description |
874 | | --- | --- |
875 | | `Array` | the slice of `array`.|
876 |
877 | ```js
878 | _.takeRight([1, 2, 3]);
879 | // => [3]
880 |
881 | _.takeRight([1, 2, 3], 2);
882 | // => [2, 3]
883 |
884 | _.takeRight([1, 2, 3], 5);
885 | // => [1, 2, 3]
886 |
887 | _.takeRight([1, 2, 3], 0);
888 | // => []
889 | ```
890 |
891 | ### takeRightWhile
892 |
893 | https://raw.githubusercontent.com/lodash/lodash/es6/array/takeRightWhile.js
894 |
895 | Creates a slice of `array` with elements taken from the end. Elements are
896 |
897 | taken until `predicate` returns falsey. The predicate is bound to `thisArg`
898 |
899 | and invoked with three arguments; (value, index, array).
900 |
901 |
902 |
903 | If a property name is provided for `predicate` the created "_.property"
904 |
905 | style callback returns the property value of the given element.
906 |
907 |
908 |
909 | If an object is provided for `predicate` the created "_.matches" style
910 |
911 | callback returns `true` for elements that have the properties of the given
912 |
913 | object, else `false`.
914 |
915 | #### Parameters
916 |
917 | | Name | Type | Description |
918 | | --- | --- | --- |
919 | | `array` | `Array` | The array to query.|
920 | | `predicate` | `Function|Object|string` | The function invoked
921 | per element.|
922 | | `thisArg` | `*` | The `this` binding of `predicate`.|
923 |
924 |
925 | #### Returns
926 |
927 | | Type | Description |
928 | | --- | --- |
929 | | `Array` | the slice of `array`.|
930 |
931 | ```js
932 | _.takeRightWhile([1, 2, 3], function(n) { return n > 1; });
933 | // => [2, 3]
934 |
935 | var users = [
936 | { 'user': 'barney', 'status': 'busy', 'active': false },
937 | { 'user': 'fred', 'status': 'busy', 'active': true },
938 | { 'user': 'pebbles', 'status': 'away', 'active': true }
939 | ];
940 |
941 | // using the "_.property" callback shorthand
942 | _.pluck(_.takeRightWhile(users, 'active'), 'user');
943 | // => ['fred', 'pebbles']
944 |
945 | // using the "_.matches" callback shorthand
946 | _.pluck(_.takeRightWhile(users, { 'status': 'away' }), 'user');
947 | // => ['pebbles']
948 | ```
949 |
950 | ### takeWhile
951 |
952 | https://raw.githubusercontent.com/lodash/lodash/es6/array/takeWhile.js
953 |
954 | Creates a slice of `array` with elements taken from the beginning. Elements
955 |
956 | are taken until `predicate` returns falsey. The predicate is bound to
957 |
958 | `thisArg` and invoked with three arguments; (value, index, array).
959 |
960 |
961 |
962 | If a property name is provided for `predicate` the created "_.property"
963 |
964 | style callback returns the property value of the given element.
965 |
966 |
967 |
968 | If an object is provided for `predicate` the created "_.matches" style
969 |
970 | callback returns `true` for elements that have the properties of the given
971 |
972 | object, else `false`.
973 |
974 | #### Parameters
975 |
976 | | Name | Type | Description |
977 | | --- | --- | --- |
978 | | `array` | `Array` | The array to query.|
979 | | `predicate` | `Function|Object|string` | The function invoked
980 | per element.|
981 | | `thisArg` | `*` | The `this` binding of `predicate`.|
982 |
983 |
984 | #### Returns
985 |
986 | | Type | Description |
987 | | --- | --- |
988 | | `Array` | the slice of `array`.|
989 |
990 | ```js
991 | _.takeWhile([1, 2, 3], function(n) { return n < 3; });
992 | // => [1, 2]
993 |
994 | var users = [
995 | { 'user': 'barney', 'status': 'busy', 'active': true },
996 | { 'user': 'fred', 'status': 'busy', 'active': false },
997 | { 'user': 'pebbles', 'status': 'away', 'active': true }
998 | ];
999 |
1000 | // using the "_.property" callback shorthand
1001 | _.pluck(_.takeWhile(users, 'active'), 'user');
1002 | // => ['barney']
1003 |
1004 | // using the "_.matches" callback shorthand
1005 | _.pluck(_.takeWhile(users, { 'status': 'busy' }), 'user');
1006 | // => ['barney', 'fred']
1007 | ```
1008 |
1009 | ## function
1010 |
1011 | ### ary
1012 |
1013 | https://raw.githubusercontent.com/lodash/lodash/es6/function/ary.js
1014 |
1015 | Creates a function that accepts up to `n` arguments ignoring any
1016 |
1017 | additional arguments.
1018 |
1019 | #### Parameters
1020 |
1021 | | Name | Type | Description |
1022 | | --- | --- | --- |
1023 | | `func` | `Function` | The function to cap arguments for.|
1024 | | `n` | `number` | The arity cap.|
1025 |
1026 |
1027 | #### Returns
1028 |
1029 | | Type | Description |
1030 | | --- | --- |
1031 | | `Function` | the new function.|
1032 |
1033 | ```js
1034 | _.map(['6', '8', '10'], _.ary(parseInt, 1));
1035 | // => [6, 8, 10]
1036 | ```
1037 |
1038 | ### before
1039 |
1040 | https://raw.githubusercontent.com/lodash/lodash/es6/function/before.js
1041 |
1042 | Creates a function that invokes `func`, with the `this` binding and arguments
1043 |
1044 | of the created function, while it is called less than `n` times. Subsequent
1045 |
1046 | calls to the created function return the result of the last `func` invocation.
1047 |
1048 | #### Parameters
1049 |
1050 | | Name | Type | Description |
1051 | | --- | --- | --- |
1052 | | `n` | `number` | The number of calls at which `func` is no longer invoked.|
1053 | | `func` | `Function` | The function to restrict.|
1054 |
1055 |
1056 | #### Returns
1057 |
1058 | | Type | Description |
1059 | | --- | --- |
1060 | | `Function` | the new restricted function.|
1061 |
1062 | ```js
1063 | jQuery('#add').on('click', _.before(5, addContactToList));
1064 | // => allows adding up to 4 contacts to the list
1065 | ```
1066 |
1067 | ### curryRight
1068 |
1069 | https://raw.githubusercontent.com/lodash/lodash/es6/function/curryRight.js
1070 |
1071 | This method is like `_.curry` except that arguments are applied to `func`
1072 |
1073 | in the manner of `_.partialRight` instead of `_.partial`.
1074 |
1075 |
1076 |
1077 | The `_.curryRight.placeholder` value, which defaults to `_` in monolithic
1078 |
1079 | builds, may be used as a placeholder for provided arguments.
1080 |
1081 |
1082 |
1083 | **Note:** This method does not set the `length` property of curried functions.
1084 |
1085 | #### Parameters
1086 |
1087 | | Name | Type | Description |
1088 | | --- | --- | --- |
1089 | | `func` | `Function` | The function to curry.|
1090 | | `arity` | `number` | The arity of `func`.|
1091 |
1092 |
1093 | #### Returns
1094 |
1095 | | Type | Description |
1096 | | --- | --- |
1097 | | `Function` | the new curried function.|
1098 |
1099 | ```js
1100 | var abc = function(a, b, c) {
1101 | return [a, b, c];
1102 | };
1103 |
1104 | var curried = _.curryRight(abc);
1105 |
1106 | curried(3)(2)(1);
1107 | // => [1, 2, 3]
1108 |
1109 | curried(2, 3)(1);
1110 | // => [1, 2, 3]
1111 |
1112 | curried(1, 2, 3);
1113 | // => [1, 2, 3]
1114 |
1115 | // using placeholders
1116 | curried(3)(1, _)(2);
1117 | // => [1, 2, 3]
1118 | ```
1119 |
1120 | ### flow
1121 |
1122 | https://raw.githubusercontent.com/lodash/lodash/es6/function/flow.js
1123 |
1124 | Creates a function that returns the result of invoking the provided
1125 |
1126 | functions with the `this` binding of the created function, where each
1127 |
1128 | successive invocation is supplied the return value of the previous.
1129 |
1130 | #### Parameters
1131 |
1132 | | Name | Type | Description |
1133 | | --- | --- | --- |
1134 | | `funcs` | `...Function` | Functions to invoke.|
1135 |
1136 |
1137 | #### Returns
1138 |
1139 | | Type | Description |
1140 | | --- | --- |
1141 | | `Function` | the new function.|
1142 |
1143 | ```js
1144 | function add(x, y) {
1145 | return x + y;
1146 | }
1147 |
1148 | function square(n) {
1149 | return n * n;
1150 | }
1151 |
1152 | var addSquare = _.flow(add, square);
1153 | addSquare(1, 2);
1154 | // => 9
1155 | ```
1156 |
1157 | ### negate
1158 |
1159 | https://raw.githubusercontent.com/lodash/lodash/es6/function/negate.js
1160 |
1161 | Creates a function that negates the result of the predicate `func`. The
1162 |
1163 | `func` predicate is invoked with the `this` binding and arguments of the
1164 |
1165 | created function.
1166 |
1167 | #### Parameters
1168 |
1169 | | Name | Type | Description |
1170 | | --- | --- | --- |
1171 | | `predicate` | `Function` | The predicate to negate.|
1172 |
1173 |
1174 | #### Returns
1175 |
1176 | | Type | Description |
1177 | | --- | --- |
1178 | | `Function` | the new function.|
1179 |
1180 | ```js
1181 | function isEven(n) {
1182 | return n % 2 == 0;
1183 | }
1184 |
1185 | _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven));
1186 | // => [1, 3, 5]
1187 | ```
1188 |
1189 | ### rearg
1190 |
1191 | https://raw.githubusercontent.com/lodash/lodash/es6/function/rearg.js
1192 |
1193 | Creates a function that invokes `func` with arguments arranged according
1194 |
1195 | to the specified indexes where the argument value at the first index is
1196 |
1197 | provided as the first argument, the argument value at the second index is
1198 |
1199 | provided as the second argument, and so on.
1200 |
1201 | #### Parameters
1202 |
1203 | | Name | Type | Description |
1204 | | --- | --- | --- |
1205 | | `func` | `Function` | The function to rearrange arguments for.|
1206 | | `indexes` | `...(number|number[])` | The arranged argument indexes,
1207 | specified as individual indexes or arrays of indexes.|
1208 |
1209 |
1210 | #### Returns
1211 |
1212 | | Type | Description |
1213 | | --- | --- |
1214 | | `Function` | the new function.|
1215 |
1216 | ```js
1217 | var rearged = _.rearg(function(a, b, c) {
1218 | return [a, b, c];
1219 | }, 2, 0, 1);
1220 |
1221 | rearged('b', 'c', 'a')
1222 | // => ['a', 'b', 'c']
1223 |
1224 | var map = _.rearg(_.map, [1, 0]);
1225 | map(function(n) { return n * 3; }, [1, 2, 3]);
1226 | // => [3, 6, 9]
1227 | ```
1228 |
1229 | ## lang
1230 |
1231 | ### isError
1232 |
1233 | https://raw.githubusercontent.com/lodash/lodash/es6/lang/isError.js
1234 |
1235 | Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`,
1236 |
1237 | `SyntaxError`, `TypeError`, or `URIError` object.
1238 |
1239 | #### Parameters
1240 |
1241 | | Name | Type | Description |
1242 | | --- | --- | --- |
1243 | | `value` | `*` | The value to check.|
1244 |
1245 |
1246 | #### Returns
1247 |
1248 | | Type | Description |
1249 | | --- | --- |
1250 | | `boolean` | `true` if `value` is an error object, else `false`.|
1251 |
1252 | ```js
1253 | _.isError(new Error);
1254 | // => true
1255 |
1256 | _.isError(Error);
1257 | // => false
1258 | ```
1259 |
1260 | ### isMatch
1261 |
1262 | https://raw.githubusercontent.com/lodash/lodash/es6/lang/isMatch.js
1263 |
1264 | Performs a deep comparison between `object` and `source` to determine if
1265 |
1266 | `object` contains equivalent property values. If `customizer` is provided
1267 |
1268 | it is invoked to compare values. If `customizer` returns `undefined`
1269 |
1270 | comparisons are handled by the method instead. The `customizer` is bound
1271 |
1272 | to `thisArg` and invoked with three arguments; (value, other, index|key).
1273 |
1274 |
1275 |
1276 | **Note:** This method supports comparing properties of arrays, booleans,
1277 |
1278 | `Date` objects, numbers, `Object` objects, regexes, and strings. Functions
1279 |
1280 | and DOM nodes are **not** supported. Provide a customizer function to extend
1281 |
1282 | support for comparing other values.
1283 |
1284 | #### Parameters
1285 |
1286 | | Name | Type | Description |
1287 | | --- | --- | --- |
1288 | | `source` | `Object` | The object to inspect.|
1289 | | `source` | `Object` | The object of property values to match.|
1290 | | `customizer` | `Function` | The function to customize comparing values.|
1291 | | `thisArg` | `*` | The `this` binding of `customizer`.|
1292 |
1293 |
1294 | #### Returns
1295 |
1296 | | Type | Description |
1297 | | --- | --- |
1298 | | `boolean` | `true` if `object` is a match, else `false`.|
1299 |
1300 | ```js
1301 | var object = { 'user': 'fred', 'age': 40 };
1302 |
1303 | _.isMatch(object, { 'age': 40 });
1304 | // => true
1305 |
1306 | _.isMatch(object, { 'age': 36 });
1307 | // => false
1308 |
1309 | // using a customizer callback
1310 | var object = { 'greeting': 'hello' };
1311 | var source = { 'greeting': 'hi' };
1312 |
1313 | _.isMatch(object, source, function(value, other) {
1314 | return _.every([value, other], RegExp.prototype.test, /^h(?:i|ello)$/) || undefined;
1315 | });
1316 | // => true
1317 | ```
1318 |
1319 | ### isNative
1320 |
1321 | https://raw.githubusercontent.com/lodash/lodash/es6/lang/isNative.js
1322 |
1323 | Checks if `value` is a native function.
1324 |
1325 | #### Parameters
1326 |
1327 | | Name | Type | Description |
1328 | | --- | --- | --- |
1329 | | `value` | `*` | The value to check.|
1330 |
1331 |
1332 | #### Returns
1333 |
1334 | | Type | Description |
1335 | | --- | --- |
1336 | | `boolean` | `true` if `value` is a native function, else `false`.|
1337 |
1338 | ```js
1339 | _.isNative(Array.prototype.push);
1340 | // => true
1341 |
1342 | _.isNative(_);
1343 | // => false
1344 | ```
1345 |
1346 | ### isTypedArray
1347 |
1348 | https://raw.githubusercontent.com/lodash/lodash/es6/lang/isTypedArray.js
1349 |
1350 | Checks if `value` is classified as a typed array.
1351 |
1352 | #### Parameters
1353 |
1354 | | Name | Type | Description |
1355 | | --- | --- | --- |
1356 | | `value` | `*` | The value to check.|
1357 |
1358 |
1359 | #### Returns
1360 |
1361 | | Type | Description |
1362 | | --- | --- |
1363 | | `boolean` | `true` if `value` is correctly classified, else `false`.|
1364 |
1365 | ```js
1366 | _.isTypedArray(new Uint8Array);
1367 | // => true
1368 |
1369 | _.isTypedArray([]);
1370 | // => false
1371 | ```
1372 |
1373 | ### toPlainObject
1374 |
1375 | https://raw.githubusercontent.com/lodash/lodash/es6/lang/toPlainObject.js
1376 |
1377 | Converts `value` to a plain object flattening inherited enumerable
1378 |
1379 | properties of `value` to own properties of the plain object.
1380 |
1381 | #### Parameters
1382 |
1383 | | Name | Type | Description |
1384 | | --- | --- | --- |
1385 | | `value` | `*` | The value to convert.|
1386 |
1387 |
1388 | #### Returns
1389 |
1390 | | Type | Description |
1391 | | --- | --- |
1392 | | `Object` | the converted plain object.|
1393 |
1394 | ```js
1395 | function Foo() {
1396 | this.b = 2;
1397 | }
1398 |
1399 | Foo.prototype.c = 3;
1400 |
1401 | _.assign({ 'a': 1 }, new Foo);
1402 | // => { 'a': 1, 'b': 2 }
1403 |
1404 | _.assign({ 'a': 1 }, _.toPlainObject(new Foo));
1405 | // => { 'a': 1, 'b': 2, 'c': 3 }
1406 | ```
1407 |
1408 | ## utility
1409 |
1410 | ### attempt
1411 |
1412 | https://raw.githubusercontent.com/lodash/lodash/es6/utility/attempt.js
1413 |
1414 | Attempts to invoke `func`, returning either the result or the caught
1415 |
1416 | error object.
1417 |
1418 | #### Parameters
1419 |
1420 | | Name | Type | Description |
1421 | | --- | --- | --- |
1422 | | `func` | `*` | The function to attempt.|
1423 |
1424 |
1425 | #### Returns
1426 |
1427 | | Type | Description |
1428 | | --- | --- |
1429 | | `*` | the `func` result or error object.|
1430 |
1431 | ```js
1432 | // avoid throwing errors for invalid selectors
1433 | var elements = _.attempt(function() {
1434 | return document.querySelectorAll(selector);
1435 | });
1436 |
1437 | if (_.isError(elements)) {
1438 | elements = [];
1439 | }
1440 | ```
1441 |
1442 | ### matches
1443 |
1444 | https://raw.githubusercontent.com/lodash/lodash/es6/utility/matches.js
1445 |
1446 | Creates a function which performs a deep comparison between a given object
1447 |
1448 | and `source`, returning `true` if the given object has equivalent property
1449 |
1450 | values, else `false`.
1451 |
1452 | #### Parameters
1453 |
1454 | | Name | Type | Description |
1455 | | --- | --- | --- |
1456 | | `source` | `Object` | The object of property values to match.|
1457 |
1458 |
1459 | #### Returns
1460 |
1461 | | Type | Description |
1462 | | --- | --- |
1463 | | `Function` | the new function.|
1464 |
1465 | ```js
1466 | var users = [
1467 | { 'user': 'fred', 'age': 40 },
1468 | { 'user': 'barney', 'age': 36 }
1469 | ];
1470 |
1471 | var matchesAge = _.matches({ 'age': 36 });
1472 |
1473 | _.filter(users, matchesAge);
1474 | // => [{ 'user': 'barney', 'age': 36 }]
1475 |
1476 | _.find(users, matchesAge);
1477 | // => { 'user': 'barney', 'age': 36 }
1478 | ```
1479 |
1480 | ### propertyOf
1481 |
1482 | https://raw.githubusercontent.com/lodash/lodash/es6/utility/propertyOf.js
1483 |
1484 | The inverse of `_.property`; this method creates a function which returns
1485 |
1486 | the property value of a given key on `object`.
1487 |
1488 | #### Parameters
1489 |
1490 | | Name | Type | Description |
1491 | | --- | --- | --- |
1492 | | `object` | `Object` | The object to inspect.|
1493 |
1494 |
1495 | #### Returns
1496 |
1497 | | Type | Description |
1498 | | --- | --- |
1499 | | `Function` | the new function.|
1500 |
1501 | ```js
1502 | var object = { 'user': 'fred', 'age': 40, 'active': true };
1503 | _.map(['active', 'user'], _.propertyOf(object));
1504 | // => [true, 'fred']
1505 |
1506 | var object = { 'a': 3, 'b': 1, 'c': 2 };
1507 | _.sortBy(['a', 'b', 'c'], _.propertyOf(object));
1508 | // => ['b', 'c', 'a']
1509 | ```
1510 |
1511 | ## collection
1512 |
1513 | ### partition
1514 |
1515 | https://raw.githubusercontent.com/lodash/lodash/es6/collection/partition.js
1516 |
1517 | Creates an array of elements split into two groups, the first of which
1518 |
1519 | contains elements `predicate` returns truthy for, while the second of which
1520 |
1521 | contains elements `predicate` returns falsey for. The predicate is bound
1522 |
1523 | to `thisArg` and invoked with three arguments; (value, index|key, collection).
1524 |
1525 |
1526 |
1527 | If a property name is provided for `predicate` the created "_.property"
1528 |
1529 | style callback returns the property value of the given element.
1530 |
1531 |
1532 |
1533 | If an object is provided for `predicate` the created "_.matches" style
1534 |
1535 | callback returns `true` for elements that have the properties of the given
1536 |
1537 | object, else `false`.
1538 |
1539 | #### Parameters
1540 |
1541 | | Name | Type | Description |
1542 | | --- | --- | --- |
1543 | | `collection` | `Array|Object|string` | The collection to iterate over.|
1544 | | `predicate` | `Function|Object|string` | The function invoked
1545 | per iteration. If a property name or object is provided it is used to
1546 | create a "_.property" or "_.matches" style callback respectively.|
1547 | | `thisArg` | `*` | The `this` binding of `predicate`.|
1548 |
1549 |
1550 | #### Returns
1551 |
1552 | | Type | Description |
1553 | | --- | --- |
1554 | | `Array` | the array of grouped elements.|
1555 |
1556 | ```js
1557 | _.partition([1, 2, 3], function(n) { return n % 2; });
1558 | // => [[1, 3], [2]]
1559 |
1560 | _.partition([1.2, 2.3, 3.4], function(n) { return this.floor(n) % 2; }, Math);
1561 | // => [[1, 3], [2]]
1562 |
1563 | var users = [
1564 | { 'user': 'barney', 'age': 36, 'active': false },
1565 | { 'user': 'fred', 'age': 40, 'active': true },
1566 | { 'user': 'pebbles', 'age': 1, 'active': false }
1567 | ];
1568 |
1569 | // using the "_.matches" callback shorthand
1570 | _.map(_.partition(users, { 'age': 1 }), function(array) { return _.pluck(array, 'user'); });
1571 | // => [['pebbles'], ['barney', 'fred']]
1572 |
1573 | // using the "_.property" callback shorthand
1574 | _.map(_.partition(users, 'active'), function(array) { return _.pluck(array, 'user'); });
1575 | // => [['fred'], ['barney', 'pebbles']]
1576 | ```
1577 |
1578 | ### sortByAll
1579 |
1580 | https://raw.githubusercontent.com/lodash/lodash/es6/collection/sortByAll.js
1581 |
1582 | This method is like `_.sortBy` except that it sorts by property names
1583 |
1584 | instead of an iteratee function.
1585 |
1586 | #### Parameters
1587 |
1588 | | Name | Type | Description |
1589 | | --- | --- | --- |
1590 | | `collection` | `Array|Object|string` | The collection to iterate over.|
1591 | | `props` | `...(string|string[])` | The property names to sort by,
1592 | specified as individual property names or arrays of property names.|
1593 |
1594 |
1595 | #### Returns
1596 |
1597 | | Type | Description |
1598 | | --- | --- |
1599 | | `Array` | the new sorted array.|
1600 |
1601 | ```js
1602 | var users = [
1603 | { 'user': 'barney', 'age': 36 },
1604 | { 'user': 'fred', 'age': 40 },
1605 | { 'user': 'barney', 'age': 26 },
1606 | { 'user': 'fred', 'age': 30 }
1607 | ];
1608 |
1609 | _.map(_.sortByAll(users, ['user', 'age']), _.values);
1610 | // => [['barney', 26], ['barney', 36], ['fred', 30], ['fred', 40]]
1611 | ```
1612 |
1613 | ## object
1614 |
1615 | ### keysIn
1616 |
1617 | https://raw.githubusercontent.com/lodash/lodash/es6/object/keysIn.js
1618 |
1619 | Creates an array of the own and inherited enumerable property names of `object`.
1620 |
1621 |
1622 |
1623 | **Note:** Non-object values are coerced to objects.
1624 |
1625 | #### Parameters
1626 |
1627 | | Name | Type | Description |
1628 | | --- | --- | --- |
1629 | | `object` | `Object` | The object to inspect.|
1630 |
1631 |
1632 | #### Returns
1633 |
1634 | | Type | Description |
1635 | | --- | --- |
1636 | | `Array` | the array of property names.|
1637 |
1638 | ```js
1639 | function Foo() {
1640 | this.a = 1;
1641 | this.b = 2;
1642 | }
1643 |
1644 | Foo.prototype.c = 3;
1645 |
1646 | _.keysIn(new Foo);
1647 | // => ['a', 'b', 'c'] (iteration order is not guaranteed)
1648 | ```
1649 |
1650 | ### valuesIn
1651 |
1652 | https://raw.githubusercontent.com/lodash/lodash/es6/object/valuesIn.js
1653 |
1654 | Creates an array of the own and inherited enumerable property values
1655 |
1656 | of `object`.
1657 |
1658 |
1659 |
1660 | **Note:** Non-object values are coerced to objects.
1661 |
1662 | #### Parameters
1663 |
1664 | | Name | Type | Description |
1665 | | --- | --- | --- |
1666 | | `object` | `Object` | The object to query.|
1667 |
1668 |
1669 | #### Returns
1670 |
1671 | | Type | Description |
1672 | | --- | --- |
1673 | | `Array` | the array of property values.|
1674 |
1675 | ```js
1676 | function Foo() {
1677 | this.a = 1;
1678 | this.b = 2;
1679 | }
1680 |
1681 | Foo.prototype.c = 3;
1682 |
1683 | _.valuesIn(new Foo);
1684 | // => [1, 2, 3] (iteration order is not guaranteed)
1685 | ```
1686 |
1687 | ## chain
1688 |
1689 | ### thru
1690 |
1691 | https://raw.githubusercontent.com/lodash/lodash/es6/chain/thru.js
1692 |
1693 | This method is like `_.tap` except that it returns the result of `interceptor`.
1694 |
1695 | #### Parameters
1696 |
1697 | | Name | Type | Description |
1698 | | --- | --- | --- |
1699 | | `value` | `*` | The value to provide to `interceptor`.|
1700 | | `interceptor` | `Function` | The function to invoke.|
1701 | | `thisArg` | `*` | The `this` binding of `interceptor`.|
1702 |
1703 |
1704 | #### Returns
1705 |
1706 | | Type | Description |
1707 | | --- | --- |
1708 | | `*` | the result of `interceptor`.|
1709 |
1710 | ```js
1711 | _([1, 2, 3])
1712 | .last()
1713 | .thru(function(value) { return [value]; })
1714 | .value();
1715 | // => [3]
1716 | ```
1717 |
1718 |
--------------------------------------------------------------------------------
/posts/harder-better-faster-stronger-lo-dash-v3/index.md:
--------------------------------------------------------------------------------
1 | [Lo-Dash](https://lodash.com/) is a utility library delivering consistency, customization, performance & extras. It is also one the most dependent upon NPM packages.[^https://www.npmjs.com/browse/depended] After a complete overhaul (over 800 commits since it has been bumped to v3.0.0-pre[^https://github.com/lodash/lodash/commit/1c770a3c66ef317eb6162fa121f6a46c3226d67f]), version 3 boosts [increased performance](#performance-improvements) and a whole lot of [new features](#added-methods).
2 |
3 | [3.0.0 version release notes](https://github.com/lodash/lodash/releases/tag/3.0.0) summarize the changes.
4 |
5 | ## Release Date
6 |
7 | Lo-Dash 3.0.0 ([lodash](#name)) has been released on 2015-01-26.[^https://twitter.com/jdalton/status/559771281705287680]
8 |
9 | ## Download
10 |
11 | Version 3.0.0 (2015-01-26) is available from the [NPM](https://www.npmjs.com/package/lodash):
12 |
13 | ```bash
14 | npm install lodash
15 | ```
16 |
17 | ### Module Formats
18 |
19 | lodash is also available in a variety of other builds & module formats.
20 |
21 | * npm packages for [modern](https://www.npmjs.com/package/lodash), [compatibility](https://www.npmjs.com/package/lodash-compat), & [per method](https://www.npmjs.com/browse/keyword/lodash-modularized) builds
22 | * AMD modules for [modern](https://github.com/lodash/lodash/tree/3.0.0-amd) & [compatibility](https://github.com/lodash/lodash-compat/tree/3.0.0-amd) builds
23 | * ES modules for the [modern](https://github.com/lodash/lodash/tree/3.0.0-es) build
24 |
25 | ## Name
26 |
27 | With version 3.0.0, "Lo-Dash" has been renamed to "lodash".[^https://github.com/lodash/lodash/commit/ae98b995698ff5432656786b9e622953a3b28669][^https://twitter.com/jdalton/status/559090662209880064]
28 |
29 | ## Breaking Changes
30 |
31 | This post focuses on the new features. For breaking changes, refer to the [changelog](https://github.com/lodash/lodash/wiki/Changelog).
32 |
33 | ### Migration
34 |
35 | There is a [lodash-migrate](https://www.npmjs.com/package/lodash-migrate) package that assists with migrating code from older lodash versions to the latest release.
36 |
37 | ## Performance Improvements
38 |
39 | > We have improved performance 20-40% overall in v3 by better utilizing the JIT in JavaScript engines, using internal helper functions that avoid optimization disqualifications & increase the likelihood of function inlining.
40 | >
41 | > We are using Set & WeakMap for performance gains. This gives all modern browsers a boost & also means lodash will have significantly faster array/function methods in io.js over Node.js because io.js has Set/WeakMap on by default.[^https://github.com/lodash/lodash/releases/tag/3.0.0]
42 |
43 | * Improved overall performance 20-40%.[^https://twitter.com/jdalton/status/560110103148257282]
44 | * Method chaining supports [lazy evaluation](#lazy-evaluation).
45 | * Methods with support for [shortcut fusion](#shortcut-fusion):
46 | * _.drop
47 | * _.dropRight
48 | * _.dropRightWhile
49 | * _.dropWhile
50 | * _.filter
51 | * _.first
52 | * _.initial
53 | * _.last
54 | * _.map
55 | * _.pluck
56 | * _.reject
57 | * _.rest
58 | * _.reverse
59 | * _.slice
60 | * _.take
61 | * _.takeRight
62 | * _.takeRightWhile
63 | * _.takeWhile
64 | * _.where
65 | * Other optimized methods:
66 | * _.bind
67 | * _.clone
68 | * _.cloneDeep
69 | * _.compact
70 | * _.compose
71 | * _.contains
72 | * _.difference
73 | * _.escape
74 | * _.flatten
75 | * _.invoke
76 | * _.isEqual
77 | * _.isObject
78 | * _.matches
79 | * _.max
80 | * _.min
81 | * _.partial
82 | * _.shuffle
83 | * _.unescape
84 | * _.uniq
85 | * _.without
86 | * _.zip
87 |
88 | ## Custom Builds
89 |
90 | The official [changelog](https://github.com/lodash/lodash-cli/wiki/Changelog) have not been updated. Until that day, I have summarized the findings[^https://github.com/lodash/grunt-lodash/issues/15].
91 |
92 | Refer to the official [changelog](https://github.com/lodash/lodash-cli/wiki/Changelog).
93 |
94 | ## ES6
95 |
96 | There is a [ES6 branch](https://github.com/lodash/lodash/tree/es) of Lo-Dash v3. It is using [ES6 modules](http://jsmodules.io/), Set & WeakMap, supports cloning typed arrays & aligns many methods to ES6.[^https://twitter.com/jdalton/status/541379703169220608]
97 |
98 | ## io.js
99 |
100 | Lo-Dash is [tested](https://travis-ci.org/lodash/lodash) and works with io.js.[^https://twitter.com/jdalton/status/555302574585155585]
101 |
102 | ## Lazy Evaluation
103 |
104 | In contrast to [eager evaluation](http://en.wikipedia.org/wiki/Eager_evaluation), lazy evaluation is an evaluation strategy which delays the evaluation of an expression until it is needed[^http://en.wikipedia.org/wiki/Lazy_evaluation]. Lo-Dash is using [lazy evaluation](http://en.wikipedia.org/wiki/Lazy_evaluation) to optimize the number of cycles needed to perform an operation. When result set sorting is not important and only a subset of the data is needed, this will result in a smaller memory footprint and smaller number of cycles.
105 |
106 | ### Example
107 |
108 | You have a collection of 100,000 products. Each product is described using a price. You need to get 5 products whose price is less than 0.5. There are two functions that are used to derive the product price, `derivePrice1` and `derivePrice2`.
109 |
110 | *Eager evaluation* will derive price of each product before proceeding to filter the collection.
111 |
112 |
113 |
114 | *Lazy evaluation* applies both functions to each member of the collection until `filter` and `take` conditions are satisfied.
115 |
116 |
117 |
118 | ### Shortcut Fusion
119 |
120 | Lazy evaluation uses [pipeline](http://en.wikipedia.org/wiki/Pipeline_%28computing%29) to process the intermediate data. Performing operation in a pipeline gets away without creating an array to store the intermediate results. Accessing the array once reduces the overhead, esp. when the data source is large and accessing memory is expensive.
121 |
122 | This does not affect Lo-Dash API. However, it is useful to know that your clunky array is handled in a resource efficient manner.
123 |
124 |
125 |
126 | ### Deferred Execution
127 |
128 | Lazy evaluation does not compute the chain until `.value()` is called (explicitly or implicitly). The benefit is that you can prepare a complex query and delay the execution until the time it is needed.
129 |
130 |
131 |
132 | ### Summary
133 |
134 | In some scenarios, lazy evaluation reduces the footprint of the application and the execution time. [Filip Zawada](https://twitter.com/filip_zawada) introduced lazy evaluation to Lo-Dash. He has written a [blog post](http://filimanjaro.com/blog/2014/introducing-lazy-evaluation/) about lazy evaluation in the context of Lo-Dash.
135 |
136 | Lazy evaluation is not a new concept in JavaScript. [lazy.js](http://danieltao.com/lazy.js/) is another utility library that implements lazy evaluation. Nevertheless, lazy evaluation is a nice addition to Lo-Dash that does not affect the API.
137 |
138 | ## Added Methods
139 |
140 | 47 new methods have been added.[^https://github.com/lodash/lodash/wiki/Changelog#notable-changes] The official documentation has not been released. I have written a [node script](https://github.com/gajus/blog.gajus.com/tree/master/post/lodash-v3/documentation-generator) that pulls each module from the [ES6 branch](https://github.com/lodash/lodash/tree/es) and parses the comments to [generate documentation](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md).
141 |
142 | ### String
143 |
144 | | Name | Description |
145 | | --- | --- |
146 | | [`camelCase`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#camelcase)| Converts `string` to camel case. See [Wikipedia](http://en.wikipedia.org/wiki/CamelCase) for more details. |
147 | | [`capitalize`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#capitalize)| Capitalizes the first character of `string`. |
148 | | [`deburr`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#deburr)| Used to match latin-1 supplementary letters (excluding mathematical operators). |
149 | | [`endsWith`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#endswith)| Checks if `string` ends with the given target string. |
150 | | [`escapeRegExp`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#escaperegexp)| Used to match `RegExp` special characters. See this [article on `RegExp` characters](http://www.regular-expressions.info/characters.html#special) for more details. |
151 | | [`kebabCase`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#kebabcase)| Converts `string` to kebab case (a.k.a. spinal case). See [Wikipedia](http://en.wikipedia.org/wiki/Letter_case#Computers) for more details. |
152 | | [`pad`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#pad)| Native method references. |
153 | | [`padLeft`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#padleft)| Pads `string` on the left side if it is shorter then the given padding length. The `chars` string may be truncated if the number of padding characters exceeds the padding length. |
154 | | [`padRight`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#padright)| Pads `string` on the right side if it is shorter then the given padding length. The `chars` string may be truncated if the number of padding characters exceeds the padding length. |
155 | | [`repeat`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#repeat)| Native method references. |
156 | | [`snakeCase`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#snakecase)| Converts `string` to snake case. See [Wikipedia](http://en.wikipedia.org/wiki/Snake_case) for more details. |
157 | | [`startsWith`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#startswith)| Checks if `string` starts with the given target string. |
158 | | [`trim`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#trim)| Removes leading and trailing whitespace or specified characters from `string`. |
159 | | [`trimLeft`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#trimleft)| Removes leading whitespace or specified characters from `string`. |
160 | | [`trimRight`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#trimright)| Removes trailing whitespace or specified characters from `string`. |
161 | | [`trunc`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#trunc)| Used as default options for `_.trunc`. |
162 | | [`words`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#words)| Used to match words to create compound words. |
163 |
164 | ### Array
165 |
166 | | Name | Description |
167 | | --- | --- |
168 | | [`chunk`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#chunk)| Native method references. |
169 | | [`dropRight`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#dropright)| Creates a slice of `array` with `n` elements dropped from the end. |
170 | | [`dropRightWhile`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#droprightwhile)| Creates a slice of `array` excluding elements dropped from the end. Elements are dropped until `predicate` returns falsey. The predicate is bound to `thisArg` and invoked with three arguments; (value, index, array). |
171 | | [`dropWhile`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#dropwhile)| Creates a slice of `array` excluding elements dropped from the beginning. Elements are dropped until `predicate` returns falsey. The predicate is bound to `thisArg` and invoked with three arguments; (value, index, array). |
172 | | [`flattenDeep`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#flattendeep)| Recursively flattens a nested array. |
173 | | [`pullAt`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#pullat)| Removes elements from `array` corresponding to the given indexes and returns an array of the removed elements. Indexes may be specified as an array of indexes or as individual arguments. **Note:** Unlike `_.at`, this method mutates `array`. |
174 | | [`slice`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#slice)| Creates a slice of `array` from `start` up to, but not including, `end`. **Note:** This function is used instead of `Array#slice` to support node lists in IE < 9 and to ensure dense arrays are returned. |
175 | | [`sortedLastIndex`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#sortedlastindex)| This method is like `_.sortedIndex` except that it returns the highest index at which `value` should be inserted into `array` in order to maintain its sort order. |
176 | | [`takeRight`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#takeright)| Creates a slice of `array` with `n` elements taken from the end. |
177 | | [`takeRightWhile`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#takerightwhile)| Creates a slice of `array` with elements taken from the end. Elements are taken until `predicate` returns falsey. The predicate is bound to `thisArg` and invoked with three arguments; (value, index, array). |
178 | | [`takeWhile`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#takewhile)| Creates a slice of `array` with elements taken from the beginning. Elements are taken until `predicate` returns falsey. The predicate is bound to `thisArg` and invoked with three arguments; (value, index, array). |
179 |
180 | ### Function
181 |
182 | | Name | Description |
183 | | --- | --- |
184 | | [`ary`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#ary)| Used to compose bitmasks for wrapper metadata. |
185 | | [`before`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#before)| Used as the `TypeError` message for "Functions" methods. |
186 | | [`curryRight`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#curryright)| Used to compose bitmasks for wrapper metadata. |
187 | | [`flow`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#flow)| Used as the `TypeError` message for "Functions" methods. |
188 | | [`negate`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#negate)| Used as the `TypeError` message for "Functions" methods. |
189 | | [`rearg`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#rearg)| Used to compose bitmasks for wrapper metadata. |
190 |
191 | ### Lang
192 |
193 | | Name | Description |
194 | | --- | --- |
195 | | [`isError`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#iserror)| `Object#toString` result references. |
196 | | [`isMatch`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#ismatch)| Used for native method references. |
197 | | [`isNative`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#isnative)| `Object#toString` result references. |
198 | | [`isTypedArray`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#istypedarray)| `Object#toString` result references. |
199 | | [`toPlainObject`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#toplainobject)| Converts `value` to a plain object flattening inherited enumerable properties of `value` to own properties of the plain object. |
200 |
201 | ### Utility
202 |
203 | | Name | Description |
204 | | --- | --- |
205 | | [`attempt`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#attempt)| Attempts to invoke `func`, returning either the result or the caught error object. |
206 | | [`matches`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#matches)| Creates a function which performs a deep comparison between a given object and `source`, returning `true` if the given object has equivalent property values, else `false`. |
207 | | [`propertyOf`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#propertyof)| The inverse of `_.property`; this method creates a function which returns the property value of a given key on `object`. |
208 |
209 | ### Collection
210 |
211 | | Name | Description |
212 | | --- | --- |
213 | | [`partition`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#partition)| Creates an array of elements split into two groups, the first of which contains elements `predicate` returns truthy for, while the second of which contains elements `predicate` returns falsey for. The predicate is bound to `thisArg` and invoked with three arguments; (value, index|key, collection). |
214 | | [`sortByAll`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#sortbyall)| This method is like `_.sortBy` except that it sorts by property names instead of an iteratee function. |
215 |
216 | ### Object
217 |
218 | | Name | Description |
219 | | --- | --- |
220 | | [`keysIn`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#keysin)| Used for native method references. |
221 | | [`valuesIn`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#valuesin)| Creates an array of the own and inherited enumerable property values of `object`. **Note:** Non-object values are coerced to objects. |
222 |
223 | ### Chain
224 |
225 | | Name | Description |
226 | | --- | --- |
227 | | [`thru`](https://github.com/gajus/blog.gajus.com/blob/master/post/harder-better-faster-stronger-lo-dash-v3/documentation.md#thru)| This method is like `_.tap` except that it returns the result of `interceptor`. |
228 |
229 | ## Summary
230 |
231 | Version 3 is the biggest update to Lo-Dash ever[^https://github.com/lodash/lodash/wiki/Changelog]. With ever increasing user base, modular code base, and cross-browser compatibility, Lo-Dash is the go-to utility library for years to come. With that in mind, there is an going competition with [lazy.js](https://github.com/dtao/lazy.js) and [undescore.js](http://underscorejs.org/), both of which are in active development.
232 |
--------------------------------------------------------------------------------
/posts/mysql-sinsert/index.md:
--------------------------------------------------------------------------------
1 | I am working a lot with data that is identifiable using an external ID (EUID; external unique identifier), stored locally and augmented with additional metadata, e.g.
2 |
3 | ## Playground
4 |
5 | 1. Rotten Tomatoes API [`/movies`](http://developer.rottentomatoes.com/docs/read/json/v10/Movie_Info) and the corresponding resource (movie) id is what I am referring to as EUID.
6 | 2. `title` is a local representation of the external resource.
7 | 3. `title.foo` and `title.bar` is metadata.
8 |
9 | ```sql
10 | CREATE TABLE `title` (
11 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
12 | `euid` int(10) unsigned NOT NULL,
13 | `foo` varchar(100) DEFAULT NULL,
14 | `bar` varchar(100) DEFAULT NULL,
15 | PRIMARY KEY (`id`),
16 | UNIQUE KEY `rottentomatoes_id` (`rottentomatoes_id`)
17 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
18 | ```
19 |
20 | ## Requirements
21 |
22 | We need to fetch arbitrary records from the external API and augment it with arbitrary metadata.
23 |
24 | We need to perform `UPSERT` (update, insert):
25 |
26 | 1. Find a local record that represents the external resource using EUID.
27 | 1. If record does not exist, insert a record.
28 | 2. Update the local record.
29 | 3. Return local id.
30 |
31 | Returning local id is not part of the `UPSERT` concept.
32 |
33 | ## Reason for not using EUID as the primary key
34 |
35 | EUID can be anything. It can be a string ID (e.g. `2cd05416a2cbb410VgnVCM1000000b43151a____`) or it can be an unnecessary large integer (e.g. Facebook ids go up to 2^32).
36 |
37 | 1. You want to make your data (and indexes) as small as possible. Shorter indexes are faster, not only because they require less disk space, but because they also give you more hits in the index cache, and thus fewer disk seeks.[^http://dev.mysql.com/doc/refman/5.7/en/data-size.html]
38 | 2. If you are working with multiple external data sources, you want to have consistent PK format.
39 |
40 | ## Solutions
41 |
42 | `UPSERT` can be done using `SELECT[, INSERT], UPDATE` or `INSERT ... ON DUPLICATE KEY UPDATE`.
43 |
44 | ### `SELECT[, INSERT], UPDATE`
45 |
46 | > Code examples use ES7 [ecmascript-asyncawait](https://github.com/lukehoban/ecmascript-asyncawait) syntax to perform queries. This is no different from using Promises.
47 |
48 | ```js
49 | let Writer = (db) => {
50 | let writer = {};
51 |
52 | /**
53 | * @param {Object} title
54 | * @param {String} title.id EUID
55 | * @param {String} title.foo
56 | * @param {String} title.bar
57 | * @return {Number} title id
58 | */
59 | writer.upsertTitle = async (title) => {
60 | let titles,
61 | titleId;
62 |
63 | await db.query('START TRANSACTION');
64 |
65 | titles = await db
66 | .query('SELECT `id` FROM `title` WHERE `euid` = ? LIMIT 1', [
67 | title.id
68 | ]);
69 |
70 | if (titles.length) {
71 | titleId = titles[0].id;
72 | } else {
73 | titleId = await db
74 | .query('INSERT INTO `title` SET `euid` = ?', [
75 | title.id
76 | ])
77 | .then((result) => {
78 | return result.insertId;
79 | });
80 | }
81 |
82 | await db
83 | .query('UPDATE `title` SET `foo` = ?, `bar` = ? WHERE `id` = ?', [
84 | title.foo,
85 | title.bar,
86 | titleId
87 | ]);
88 |
89 | await db.query('COMMIT');
90 |
91 | return titleId;
92 | };
93 |
94 | return writer;
95 | };
96 | ```
97 |
98 | ### `INSERT ... ON DUPLICATE KEY UPDATE`
99 |
100 | ```js
101 | let Writer = (db) => {
102 | let writer = {};
103 |
104 | /**
105 | * @param {Object} title
106 | * @param {String} title.id EUID
107 | * @param {String} title.foo
108 | * @param {String} title.bar
109 | * @return {Number} title id
110 | */
111 | writer.upsertTitle = async (title) => {
112 | let titles;
113 |
114 | await db
115 | .query('INSERT INTO `title` SET `euid` = ?, `foo` = ?, `bar` = ? ON DUPLICATE KEY UPDATE `foo` = VALUES(`foo`), `bar` = VALUES(`bar`)', [
116 | title.id,
117 | title.foo,
118 | title.bar
119 | ]);
120 |
121 | titles = await db
122 | .query('SELECT `id` FROM `title` WHERE `euid` = ? LIMIT 1', [
123 | title.id
124 | ]);
125 |
126 | return titles[0].id;
127 | };
128 |
129 | return writer;
130 | };
131 | ```
132 |
133 | ## Shortcomings
134 |
135 | ### `SELECT[, INSERT], UPDATE`
136 |
137 | * Verbose.
138 | * Requires that all other columns have a default value (or be nullable).
139 |
140 | ### `INSERT ... ON DUPLICATE KEY UPDATE`
141 |
142 | * Hard to analyzing query logs (e.g. identifying ratio between found/not found records).
143 | * `ON DUPLICATE KEY UPDATE` will not work when id is an auto-increment column.
144 | * You should try to avoid using an `ON DUPLICATE KEY UPDATE` clause on tables with multiple unique indexes.[^https://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html].
145 |
146 | ## The solution
147 |
148 | With these considerations in mind, I prefer `SELECT[, INSERT], UPDATE` approach.
149 |
150 | * Verbosity is an easy problem to abstract.
151 | * Default value/[`NULL` problem](#null-problem) has its advantages.
152 |
153 | ### `SINSERT`
154 |
155 | I wrote a function that abstracts the look up of an existing record using a unique key and inserts the record when it is not found. I am calling it `SINSERT` (`SELECT[, INSERT]`):
156 |
157 | ```js
158 | /**
159 | * Selects a record using a unique identifier.
160 | * If record is not found, inserts a record using the unique identifier.
161 | * Returns id of the inserted record.
162 | *
163 | * @param {String} table
164 | * @param {String} column
165 | * @param {String|Number} uid
166 | * @return {Number} id
167 | */
168 | db.sinsert = async (table, column, uid) => {
169 | let id;
170 |
171 | await db.query('START TRANSACTION');
172 |
173 | id = db
174 | .query('SELECT `id` FROM ?? WHERE ?? = ?', [
175 | table,
176 | column,
177 | uid
178 | ])
179 | .then((rows) => {
180 | if (rows.length) {
181 | return rows[0].id;
182 | }
183 |
184 | return db
185 | .query('INSERT INTO ?? SET ?? = ?', [
186 | table,
187 | column,
188 | uid
189 | ])
190 | .then((result) => {
191 | return result.insertId;
192 | });
193 | });
194 |
195 | await db.query('COMMIT');
196 |
197 | return id;
198 | };
199 | ```
200 |
201 | Using `SINSERT` we can continue to use EUID to retrieve reference to the local representation of the external resource, and use local id to perform business-as-usual `UPDATE` operation.
202 |
203 | ```js
204 | let Writer = (db) => {
205 | let writer = {};
206 |
207 | /**
208 | * @param {Object} title
209 | * @param {String} title.id EUID
210 | * @param {String} title.foo
211 | * @param {String} title.bar
212 | * @return {Number} title id
213 | */
214 | writer.upsertTitle = async (title) => {
215 | let titleId;
216 |
217 | titleId = await db.sinsert('title', 'euid', title.id);
218 |
219 | await db
220 | .query('UPDATE `title` SET `foo` = ?, `bar` = ? WHERE `id` = ?', [
221 | title.foo,
222 | title.bar,
223 | titleId
224 | ]);
225 |
226 | return titleId;
227 | };
228 |
229 | return writer;
230 | };
231 | ```
232 |
233 | ## `NULL` problem
234 |
235 | External resource representation in a local database must include EUID. This is the minimum knowledge required to:
236 |
237 | 1. Establish existence of the external resource.
238 | 2. Create a local representation of the resource.
239 | 3. Lookup data about the resource in the future.
240 |
241 | All metadata should be nullable to allow creating resource presence representation using the minimum available information. This is important. It enables separation of parsing and writing logic, e.g.
242 |
243 | ### TV shows, seasons and episodes
244 |
245 | You are working with an API that provides information about TV shows, seasons and episodes. You want to store information about an episode in a local database.
246 |
247 | You will need `episode` table that describes EUID, metadata and assigns a local resource id. You will have `show` and `season` tables describing the equivalent data. You can further normalise this by creating table `title` that represents the external resource collection.
248 |
249 | ```sql
250 | CREATE TABLE `title` (
251 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
252 | `euid` varchar(255) NOT NULL DEFAULT '',
253 | PRIMARY KEY (`id`),
254 | UNIQUE KEY `euid` (`euid`)
255 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
256 |
257 | CREATE TABLE `episode` (
258 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
259 | `title_id` int(10) unsigned NOT NULL,
260 | `season_id` int(10) unsigned DEFAULT NULL,
261 | `name` varchar(255) DEFAULT NULL,
262 | `number` int(10) unsigned DEFAULT NULL,
263 | PRIMARY KEY (`id`),
264 | UNIQUE KEY `season_id` (`season_id`,`number`),
265 | KEY `title_id` (`title_id`),
266 | CONSTRAINT `episode_ibfk_1` FOREIGN KEY (`title_id`) REFERENCES `title` (`id`) ON DELETE CASCADE,
267 | CONSTRAINT `episode_ibfk_2` FOREIGN KEY (`season_id`) REFERENCES `season` (`id`) ON DELETE CASCADE
268 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
269 |
270 | CREATE TABLE `season` (
271 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
272 | `title_id` int(10) unsigned NOT NULL,
273 | `show_id` int(10) unsigned DEFAULT NULL,
274 | `number` int(10) unsigned DEFAULT NULL,
275 | PRIMARY KEY (`id`),
276 | UNIQUE KEY `title_id` (`title_id`),
277 | UNIQUE KEY `show_id` (`show_id`,`number`),
278 | CONSTRAINT `season_ibfk_1` FOREIGN KEY (`title_id`) REFERENCES `title` (`id`) ON DELETE CASCADE,
279 | CONSTRAINT `season_ibfk_2` FOREIGN KEY (`show_id`) REFERENCES `show` (`id`) ON DELETE CASCADE
280 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
281 |
282 | CREATE TABLE `show` (
283 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
284 | `title_id` int(10) unsigned NOT NULL,
285 | `name` varchar(255) DEFAULT NULL,
286 | PRIMARY KEY (`id`),
287 | UNIQUE KEY `title_id` (`title_id`),
288 | CONSTRAINT `show_ibfk_1` FOREIGN KEY (`title_id`) REFERENCES `title` (`id`) ON DELETE CASCADE
289 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
290 | ```
291 |
292 | You have made an API call to retrieve information about a TV episode.
293 |
294 | ```json
295 | {
296 | "id": "a9fc6741eb003410VgnVCM1000000b43151a____",
297 | "type": "episode",
298 | "name": "The Flamingo",
299 | "number": 1,
300 | "show": {
301 | "id": "ef10b1b8e1e50410VgnVCM1000000b43151a____"
302 | },
303 | "season": {
304 | "id": "46e58fc0fbcd6410VgnVCM1000000b43151a____"
305 | }
306 | }
307 | ```
308 |
309 | You want to write this information to the database.
310 |
311 | If `show` or `season` tables had non-nullable columns (e.g. show name or season number), you would not be able to insert record about TV episode without first fetching information about the TV show and season (assuming it is not already in the database).
312 |
313 | ```js
314 | let Writer = (db) => {
315 | let writer = {};
316 |
317 | /**
318 | * @param {Object} episode
319 | * @param {String} episode.id Episode EUID.
320 | * @param {String} episode.name
321 | * @param {String} episode.number
322 | * @param {String} episode.show.id Show EUID.
323 | * @param {String} episode.season.id Season EUID.
324 | * @return {Number} episode id
325 | */
326 | writer.upsertEpisode = (episode) => {
327 | let showId = await writer.sinsertShow(episode.show.id),
328 | seasonId = await writer.sinsertSeason(episode.season.id),
329 | episodeId = await writer.sinsertEpisode(episode.id);
330 |
331 | // Update.
332 |
333 | return episodeId;
334 | };
335 |
336 | /**
337 | * @private
338 | * @param {String} EUID
339 | * @return {Number} title id
340 | */
341 | writer.sinsertTitle = (EUID) => {
342 | return db.sinsert('title', 'euid', EUID);
343 | };
344 |
345 | /**
346 | * @private
347 | * @param {String} Show EUID
348 | * @return {Number} show id
349 | */
350 | writer.sinsertShow = async (EUID) => {
351 | let titleId = await writer.sinsertTitle(EUID);
352 |
353 | return db.sinsert('show', 'title_id', titleId);
354 | };
355 |
356 | /**
357 | * @private
358 | * @param {String} Season EUID
359 | * @return {Number} season id
360 | */
361 | writer.sinsertSeason = async (EUID) => {
362 | let titleId = await writer.sinsertTitle(EUID);
363 |
364 | return db.sinsert('season', 'title_id', titleId);
365 | };
366 |
367 | /**
368 | * @private
369 | * @param {String} Episode EUID
370 | * @return {Number} episode id
371 | */
372 | writer.sinsertEpisode = async (EUID) => {
373 | let titleId = await writer.sinsertTitle(EUID);
374 |
375 | return db.sinsert('episode', 'title_id', titleId);
376 | };
377 |
378 | return writer;
379 | };
380 | ```
381 |
382 | This approach enables you to write data with complex relationships, but requires minimal knowledge about each player. Meta information about each player can be fetched in the future.
383 |
--------------------------------------------------------------------------------
/posts/pro-angularjs/index.md:
--------------------------------------------------------------------------------
1 | This article will teach you about the individual components of AngularJS (1.3) and how they come together to make an application, i.e. you will known what is an AngularJS application and how to build it. This article is biased towards the [community conventions](#conventions) and therefore ignores uncommon/ill/edge use cases (e.g. using HTML comments to reference a directive from the template). This article assumes that you have a good understanding of JavaScript.
2 |
3 | All of the examples include references to the relevant documentation. If you are struggling to understand the included examples, refer to the documentation. All of the examples are in [JSFiddle](http://jsfiddle.net/).
4 |
5 | If this is your first encounter with AngularJS, please first read the official [Developer Guide](https://docs.angularjs.org/guide/introduction). It explains what type of applications should be developed using AngularJS and what good using AngularJS achieves.
6 |
7 | ## Single-Page Applications
8 |
9 | A typical [single-page application](http://en.wikipedia.org/wiki/Single-page_application) (SPA) starts with a stateless request to a server. The response is a document that executes the application. The application will load the necessary components (e.g. fragments of HTML and data to represent the current state of the application) in response to user actions. Typically, using [AJAJ](http://en.wikipedia.org/wiki/AJAJ). The goal is to provide a more fluid user experience akin to a desktop application. The page does not reload at any point in the process.
10 |
11 | In contrast, round-trip app logic and data resides on the server side; the browser acts as a rendering engine. The browser makes a series of stateless requests that the server side responds to with dynamically generated HTML. The drawbacks are: a lot of bandwidth waste, the user need to wait until the next HTML document is requested and loaded.
12 |
13 | AngularJS is used to develop single-page applications.
14 |
15 | ## REST
16 |
17 | Single-page application requires a web-service (backend) to perform [create, read, update and delete](http://en.wikipedia.org/wiki/Create,_read,_update_and_delete) (CRUD) operations. In modern web development, such service conforms to [representational state transfer](http://en.wikipedia.org/wiki/Representational_state_transfer) (REST) architecture.
18 |
19 | Two key players are the *URL* and the *request method*. The URL identifies the resource, and the HTTP method specifies what operation (CRUD) to perform.
20 |
21 | | Method | Intent |
22 | | --- | --- |
23 | | `GET` | Retrieve object specified by the URL. `GET` method is *nullipotent* (the same operation performed 0 or more times does not change the result). |
24 | | `PUT` | Update the object specified by the URL. `PUT` operation is *idempotent* (the same operation performed 1 or more times has the same effect on the data). |
25 | | `POST` | Create a new object (can perform double duty: update an object if it exists or create a new one if not) (neither nullipotent or idempotent). |
26 | | `DELETE` | Delete the object specified by the URL (idempotent). |
27 |
28 | ## MVW
29 |
30 | AngularJS advocates [Model-View-Whatever](https://plus.google.com/+AngularJS/posts/aZNVhj355G2) architectural pattern. Angular Controllers setup and add behavior to `$scope` objects, and Angular's [templates](#templates) and two-binding make the View layer, but Angular has virtually no opinion when it comes to your Model layer. You can do *whatever* you want.[^http://www.pseudobry.com/building-large-apps-with-angular-js/]
31 |
32 | Regardless of what we are calling it, it is important to separate the presentation logic from business logic and presentation state. To this extent, we are going to be calling it MVC.
33 |
34 | ### MVC
35 |
36 | | Player | Role |
37 | | --- | --- |
38 | | [Model](#model) | Logic that deals with storing and retrieving the data. |
39 | | [View](#view) | Logic that deals with formatting data to display to the user. |
40 | | [Controller](#controller) | Logic that responds to the user interactions, updates data in the model and provides data to the view. |
41 |
42 | #### Model
43 |
44 | Model should:
45 |
46 | * Contain the logic for CRUD the domain data (even if that means executing the remote logic via web services).
47 | * Define the [data domain](http://en.wikipedia.org/wiki/Data_domain).
48 | * Provide a clean API that exposes the model data and operations on it.
49 |
50 | Model should not:
51 |
52 | * Expose the details of how the model data is obtained or managed (in other words, details of the data storage mechanism or the remote web service should not be exposed to the controllers and views).
53 | * Contain the logic that transforms the model based on user interaction (this is the controller's job).
54 | * Contain logic for displaying data to the user (this is the view's job).
55 |
56 | The benefit of isolating model from the controller and view is that you can test your application logic more easily, and that enhancing and maintaining the overall application is simpler and easier.
57 |
58 | #### Controller
59 |
60 | Controller should:
61 |
62 | * Contain the logic required to initialise the scope.
63 | * Contain the logic/behaviours required by the view to present data from the scope.
64 | * Contain the logic/behaviours required to update the scope based on user interaction.
65 |
66 | Controller should not:
67 |
68 | * Contain logic that manipulates the DOM (that is the job for the view).
69 | * Contain logic that manages the persistence of data (that is the job for the model).
70 | * Manipulate data outside of the scope.
71 |
72 | #### View
73 |
74 | Views should:
75 |
76 | * Contain the logic and markup required to present data to the user.
77 |
78 | Views should not:
79 |
80 | * Contain complex logic (this is better placed in the controller).
81 | * Contain logic that creates, stores or manipulates the domain model.
82 |
83 | ### Common design pitfalls
84 |
85 | * Putting the logic in the wrong place (e.g. business logic in the view, rather than a controller; domain logic in the controller, rather than in a model; data store logic in the client model when using a RESTful service).
86 |
87 | * Manipulating the DOM directly using jQuery.
88 |
89 | ### Three rules to avoid the pitfalls
90 |
91 | * View logic should prepare data only for display and never modify the model.
92 | * Controller should never directly create, update or delete data from the model.
93 | * The client should never directly access the database.
94 |
95 | ## Conventions
96 |
97 | Angular puts itself forth as a [MVW](#mvw) framework (TL;DR you need conventions where framework does not enforce them).
98 |
99 | These are the most popular community driven conventions (in the order of the number of stars on GitHub):
100 |
101 | * [https://github.com/johnpapa/angularjs-styleguide](https://github.com/johnpapa/angularjs-styleguide)
102 | * [https://github.com/mgechev/angularjs-style-guide](https://github.com/mgechev/angularjs-style-guide)
103 | * [https://github.com/toddmotto/angularjs-styleguide](https://github.com/toddmotto/angularjs-styleguide)
104 |
105 | If you are starting with AngularJS, follow either (the most popular) of the existing style guides. Personal advise: if you disagree with a certain aspect of the convention, raise an issue and explain your reason for doing otherwise. If the vast majority of the community disagrees with you, chances are that you are better off to just go with the flow.
106 |
107 |
108 |
109 | ## Angular Components
110 |
111 | ### Functions
112 |
113 | Angular provides a number of [utility functions](https://docs.angularjs.org/api/ng/function) for common programming tasks:
114 |
115 | * [angular.lowercase](https://docs.angularjs.org/api/ng/function/angular.lowercase)
116 | * [angular.uppercase](https://docs.angularjs.org/api/ng/function/angular.uppercase)
117 | * [angular.forEach](https://docs.angularjs.org/api/ng/function/angular.forEach)
118 | * [angular.extend](https://docs.angularjs.org/api/ng/function/angular.extend)
119 | * [angular.noop](https://docs.angularjs.org/api/ng/function/angular.noop)
120 | * [angular.identity](https://docs.angularjs.org/api/ng/function/angular.identity)
121 | * [angular.isUndefined](https://docs.angularjs.org/api/ng/function/angular.isUndefined)
122 | * [angular.isDefined](https://docs.angularjs.org/api/ng/function/angular.isDefined)
123 | * [angular.isObject](https://docs.angularjs.org/api/ng/function/angular.isObject)
124 | * [angular.isString](https://docs.angularjs.org/api/ng/function/angular.isString)
125 | * [angular.isNumber](https://docs.angularjs.org/api/ng/function/angular.isNumber)
126 | * [angular.isDate](https://docs.angularjs.org/api/ng/function/angular.isDate)
127 | * [angular.isArray](https://docs.angularjs.org/api/ng/function/angular.isArray)
128 | * [angular.isFunction](https://docs.angularjs.org/api/ng/function/angular.isFunction)
129 | * [angular.isElement](https://docs.angularjs.org/api/ng/function/angular.isElement)
130 | * [angular.copy](https://docs.angularjs.org/api/ng/function/angular.copy)
131 | * [angular.equals](https://docs.angularjs.org/api/ng/function/angular.equals)
132 | * [angular.bind](https://docs.angularjs.org/api/ng/function/angular.bind)
133 | * [angular.toJson](https://docs.angularjs.org/api/ng/function/angular.toJson)
134 | * [angular.fromJson](https://docs.angularjs.org/api/ng/function/angular.fromJson)
135 |
136 | The list is limiting and depends on the needs of the Angular core development team. For this reason, I prefer the route that the [Backbone](http://backbonejs.org/) took. Backbone.js has a hard dependency for [Underscore](http://underscorejs.org/), which is a standalone collection of utility functions. While I don't recommend Angular team to have a hard dependency on an external utility function collection, I advise against using utility functions that are hard-coded into the framework and whose primary function is to support the framework.
137 |
138 | If you are starting a large scale application, I advise to not use either of the above functions and familiarize with either of the standalone utility libraries ([lodash](https://lodash.com/), [Underscore](http://underscorejs.org/), [lazy.js](http://danieltao.com/lazy.js/)). Whatever you choose, be consistent.
139 |
140 | The following functions are specific to AngularJS:
141 |
142 | #### angular.bootstrap
143 |
144 | Use [angular.bootstrap](https://docs.angularjs.org/api/ng/function/angular.bootstrap) to manually start up angular application.
145 |
146 | The example does not use the [ngApp](https://docs.angularjs.org/api/ng/directive/ngApp) directive to auto-bootstrap an application.
147 |
148 |
149 |
150 | #### angular.reloadWithDebugInfo
151 |
152 | Use [angular.reloadWithDebugInfo](https://docs.angularjs.org/api/ng/function/angular.reloadWithDebugInfo) to reload the current application with debug information turned on.
153 |
154 | Debug mode:
155 |
156 | * Attaches information about the bindings (`$binding` property of the [element's data](http://api.jquery.com/data/); data is in the jQlite scope and not in the `HTMLElement.dataset`).
157 | * Adds CSS classes ("ng-binding") to data-bound elements (as a result of `ngBind`, `ngBindHtml` or `{{...}}` interpolations).
158 | * Where the compiler has created a new scope, the scope and either "ng-scope" or "ng-isolated-scope" CSS class are attached to the corresponding element. These scope references can then be accessed via `element.scope()` and `element.isolateScope()`.
159 |
160 | Debug information is enabled by default. This function is used when [`$compileProvider.debugInfoEnabled()`](https://docs.angularjs.org/api/ng/provider/$compileProvider#debugInfoEnabled) has been used to disable debug information.
161 |
162 | [Disable debug information](https://docs.angularjs.org/guide/production#disabling-debug-data) in production for a significant performance boost. In production, you can call `angular.reloadWithDebugInfo()` in browser console to enable debug information.
163 |
164 |
165 |
166 | #### angular.injector
167 |
168 | [angular.injector](https://docs.angularjs.org/api/ng/function/angular.injector) creates an injector object (`$injector`). [`$injector`](https://docs.angularjs.org/api/auto/service/$injector) is used to retrieve object instances as defined by provider, instantiate types, invoke methods, and load modules.
169 |
170 | Angular creates a single $injector when it bootstraps an application and uses the single $injector to invoke controller functions, service functions, filter functions, and any other function that might need dependencies as parameters.[^http://odetocode.com/blogs/scott/archive/2014/01/13/meet-the-angularjs-injector.aspx]. You can obtain an instance of the injector:
171 |
172 | * If you manually bootstrap the application (using `angular.bootstrap()`).
173 | * Using `angular.element([DOM node]).injector()` where [DOM element] is a DOM element where the `ng-app` was defined (or a child element of this element).
174 | * Using DI, e.g. `$injector` parameter in the `module.config()`.
175 |
176 | `angular.injector()` is for creating a new instance of the `$injector`.
177 |
178 | Creating your own injector is useful in unit tests where you do not want singleton service instances. You must pass a list of the modules the injector will work with (just the core "ng" module in the above code). You have to explicitly list the ng module if you are going to use any services from the core of Angular. Unlike the angular.module method, which assumes you have a dependency on the ng module and will silently add "ng" to your list of dependencies, the injector function makes no assumptions about dependent modules.[^http://odetocode.com/blogs/scott/archive/2014/01/13/meet-the-angularjs-injector.aspx]
179 |
180 |
181 |
182 | #### angular.element
183 |
184 | [angular.element](https://docs.angularjs.org/api/ng/function/angular.element) wraps a raw DOM element or HTML string as a jQuery-like element.
185 |
186 | Angular is using a [built-in subset of jQuery](https://docs.angularjs.org/api/ng/function/angular.element#angular-s-jqlite), called "jQuery lite" or "jqLite".
187 |
188 | Refer to the official documentation of [angular.element](https://docs.angularjs.org/api/ng/function/angular.element) to learn about jQuery-lite specific methods and events.
189 |
190 | #### angular.module
191 |
192 | The [angular.module](https://docs.angularjs.org/api/ng/function/angular.module) is a global place for creating, registering and retrieving Angular modules. All modules (angular core or 3rd party) that should be available to an application must be registered using this mechanism.
193 |
194 | When passed two or more arguments, a new module is created. If passed only one argument, an existing module (the name passed as the first argument to module) is retrieved.
195 |
196 | A module is a collection of services, directives, controllers, filters, and configuration information. angular.module is used to configure the $injector.
197 |
198 | ## Templates
199 |
200 | In Angular, templates are written with HTML that contains Angular-specific elements and attributes. Angular combines the template with information from the model and controller to render the dynamic view that a user sees in the browser.
201 |
202 | AngularJS extends HTML in the following ways:
203 |
204 | * [Expressions & Data binding](#expressions-data-binding).
205 | * [Directives](#directives)
206 |
207 | ### Expressions & Data Binding
208 |
209 | AngularJS uses double-brace characters (`{{` and `}}`) to denote expression. The content of the expression is evaluated as a subset of JavaScript in the context of the `scope`. Expressions are evaluated using [$interpolate](https://docs.angularjs.org/api/ng/service/$interpolate) service.
210 |
211 |
212 |
213 | Angular expressions are like JavaScript expressions with the following differences:
214 |
215 | * [Context](#context)
216 | * [Forgiving](#forgiving)
217 | * You cannot use the following in an Angular expression: conditionals, loops, or exceptions.
218 | * You cannot declare functions in an Angular expression.
219 | * You cannot create regular expressions in an Angular expression.
220 | * You cannot use `,` or `void` in an Angular expression.
221 | * You can use [filters](https://docs.angularjs.org/api/ng/filter/filter) within expressions to format data before displaying it.
222 |
223 | #### Context
224 |
225 | In Angular, expressions are evaluated against a scope object, i.e. Angular expressions do not have access to global variables like `window`, `document` or `location`.
226 |
227 | In addition to implicit variables and inherited scope variables, every scope inherits these properties from the [$rootScope](https://docs.angularjs.org/api/ng/type/$rootScope.Scope):
228 |
229 | | Property | Description |
230 | | --- | --- |
231 | | `$id` | Unique scope ID (monotonically increasing) useful for debugging. |
232 | | `$parent` | Reference to the parent scope. |
233 | | `$root` | Reference to the root scope. |
234 |
235 |
236 |
237 | #### Forgiving
238 |
239 | Angular expression that evaluates to undefined scope property does not throw an error.
240 |
241 |
242 |
243 | ## Directives
244 |
245 | * Directives are functions.
246 | * Directives are referenced in HTML templates using element-names and attributes.
247 | * Directives define a custom behavior and transform the associated DOM element(s).
248 | * Angular has [inbuilt directives](https://docs.angularjs.org/api/ng/directive).
249 | * New directives can be declared using [`module.directive`](https://docs.angularjs.org/api/ng/type/angular.Module#directive).
250 |
251 | ### Example
252 |
253 | | Directive | Role |
254 | | --- | --- |
255 | | [`ng-app`](https://docs.angularjs.org/api/ng/directive/ngApp) | [Bootstraps](#bootstrap) an Angular application. |
256 | | [`ng-bind`](https://docs.angularjs.org/api/ng/directive/ngBind) | Attribute-reference equivalent of the [double-brace expression](#expressions-data-binding). |
257 | | `gajus-bar` | Custom directive. |
258 | | `gajus-baz` | Intentionally misspelled reference to `gajusBar` directive. |
259 |
260 |
261 |
262 | #### Referencing
263 |
264 | The [HTML compiler](https://docs.angularjs.org/guide/compiler) traverses the DOM matching directives against the DOM elements. HTML compiler must know the directive name before it can be matched in DOM. Elements that have names or attributes that do not resolve to an existing directive will be left intact. In the earlier example, `ngApp`, `ngBind` and `gajusBar` are resolved; `gajus-baz` is misspelled reference to `gajusBar`. Notice that there is no error; Angular ignores unknown elements and attributes.
265 |
266 | #### Naming
267 |
268 | * Directive declaration is in camelCase (e.g. `gajusBar`).
269 | * Directive reference is dash-delimited name (e.g. `gajus-bar`).
270 | * Inbuilt directives are prefixed with `ng`.
271 | * Custom directives must be prefixed with a short, unique and descriptive prefix.[^https://github.com/johnpapa/angularjs-styleguide#style-y073]
272 |
273 |
274 |
275 | ### Inbuilt
276 |
277 | Angular has many [inbuilt directives](https://docs.angularjs.org/api/ng/directive) for:
278 |
279 | * DOM control structures (repeating/hiding/conditionally including DOM fragments).
280 | * Event handling (click, submit, etc.).
281 |
282 |
283 | ### Custom
284 |
285 | * Directives are created using the [`directive`](https://docs.angularjs.org/api/ng/type/angular.Module#directive) method on an Angular `module`.
286 | * Directives are referenced from HTML templates using element-name or attribute.
287 |
288 |
289 |
290 | In the "foo" example, I am:
291 |
292 | * Defined "foo" module.
293 | * Defined "bar" directive.
294 | * Instructed directive to:
295 | * replace the referencing element with the template.
296 | * respond to attribute reference.
297 | * use string template
298 |
299 |
302 |
303 | ### Community
304 |
305 | There are a number of Angular directives made open-source by the community available for the re-use:
306 |
307 | * [http://ngmodules.org/](http://ngmodules.org/)
308 | * [https://github.com/search?l=JavaScript&q=angular+directive&type=Repositories](https://github.com/search?l=JavaScript&q=angular+directive&type=Repositories)
309 |
310 |
333 |
334 | ## Further Reading
335 |
336 | * [Pro AngularJS](http://www.apress.com/9781430264484) by Adam Freeman.
337 | * [Write Modern Web Apps with the MEAN Stack: Mongo, Express, AngularJS, and Node.js](http://www.peachpit.com/store/write-modern-web-apps-with-the-mean-stack-mongo-express-9780133962352) by Jeff Dickey
338 | * [D3 on AngularJS](https://leanpub.com/d3angularjs) by Ari Lerner and Victor Powell
339 |
--------------------------------------------------------------------------------
/posts/the-definitive-guide-to-the-es7-async-functions/index.md:
--------------------------------------------------------------------------------
1 | ## Continuation-Passing Style
2 |
3 | Most JavaScript code that relies on asynchronous data is written using [continuation-passing style](http://en.wikipedia.org/wiki/Continuation-passing_style), e.g.
4 |
5 |
8 |
9 | There are a few things that can help to minimize the so-called callback hell (viz., using named functions over anonymous functions, modularization)[^http://callbackhell.com/]. Nonetheless, sequential iterations remains a PITA.
10 |
11 |
22 |
23 | ## ES7 `async/await`
24 |
25 | If you are comfortable working with ES6 Promise, ES7 `async/await` is the sugar-coated equivalent.
26 |
27 |
28 |
29 |
30 |
31 |
32 | `await` keyword expects [ES6 Promise](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise):
33 |
34 | * If/when promise is resolved, the return value is the resolved value.
35 | * If/when promise is resolved, the [error thrown](#error-handling) is what been used to reject the promise.
36 |
37 | You can use `await` only in `async` function.
38 |
39 | ## ES7 `await*`
40 |
41 | [`await*`](https://github.com/lukehoban/ecmascript-asyncawait#await-and-parallelism) is a syntactic sugar for [`Promise.all`](https://github.com/petkaantonov/bluebird/blob/master/API.md#all---promise).
42 |
43 |
44 |
45 | ## Running ES7 Code
46 |
47 | [Async Functions for ECMAScript proposal](https://github.com/lukehoban/ecmascript-asyncawait) was accepted into stage 1 ("Proposal") of the ECMASCript 7 [spec process](https://docs.google.com/document/d/1QbEE0BsO4lvl7NFTn5WXWeiEIBfaVUF7Dk0hpPpPDzU) in January 2014. The spec text can be found [here](http://tc39.github.io/ecmascript-asyncawait/).
48 |
49 | You can run `async/await` code using a transpiler (source-to-source compiler). [Babel](https://babeljs.io/) transpiler has the biggest coverage of ES6 and ES7 features.[^http://kangax.github.io/compat-table/es7/]
50 |
51 | ```sh
52 | npm install babel@5.0.4 -g
53 | babel-node --stage 1 ./your-script.js
54 | ```
55 |
56 | ## Choosing Between ES6 Promise and ES7 `async/await`
57 |
58 | `async/await` makes the biggest difference when you have multiple dependencies that need to be fetched asynchronously, e.g. (the requirement is to have all three connections open at once)
59 |
60 |
61 |
62 | Of course, even the latter can be flattened using Bluebird [.spread()](https://github.com/petkaantonov/bluebird/blob/master/API.md#spreadfunction-fulfilledhandler--function-rejectedhandler----promise):
63 |
64 |
65 |
66 | Nonetheless, I prefer `await` keyword:
67 |
68 |
69 |
70 | [add example showing how each/map/then chaining work together with await]
71 |
72 | ## Error Handling
73 |
74 | You must wrap all instances of `await` in a `try...catch` statement.
75 |
76 |
77 |
78 | The promise that we await for is rejected. Rejection terminates the `async` block but does not propagate to the program. The output of the above program:
79 |
80 | ```
81 | a
82 | 1
83 | b
84 | [Error: Oops #0]
85 | ```
86 |
87 | Take a moment to observe that `[Error: Oops #0]` comes after `b` and `1` does not. This is because the `async` function is defined as IIFE. To delay
88 |
89 | As the code base grows it becomes harder and harder to handle errors within `async` functions.
90 |
91 | ### Global Error Handler
92 |
93 | Remember that beneath the surface, `await` simply handles ES6 [Promise](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise).
94 |
95 | You can use [Bluebird](https://github.com/petkaantonov/bluebird) Promise implementation to take advantage of the [global rejection events](https://github.com/petkaantonov/bluebird/blob/master/API.md#global-rejection-events).
96 |
97 |
98 |
99 | ## Testing `async/await`
100 |
101 | When I started using `async/await` my first test cases were these:
102 |
103 |
104 |
105 | Remember that beneath the surface, `await` simply handles ES6 [Promise](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise).
106 |
107 | You can use your regular testing strategy that you use when testing Promise objects. I use [`chai-as-promised`](https://github.com/domenic/chai-as-promised/):
108 |
109 |
110 |
111 | My experience was that using Promise directly for testing has prooved to be more reliable then experimental `async/await`.
112 |
--------------------------------------------------------------------------------
/posts/the-definitive-guide-to-the-javascript-generators/generators.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gajus/gajus.com-blog/a91760feeb9198eb6ab32a81895fdfb31d05b7ae/posts/the-definitive-guide-to-the-javascript-generators/generators.gif
--------------------------------------------------------------------------------
/posts/the-definitive-guide-to-the-javascript-generators/index.md:
--------------------------------------------------------------------------------
1 | There are many articles [^http://odetocode.com/blogs/scott/archive/2014/02/17/thoughts-on-javascript-generators.aspx] [^http://truffles.me.uk/playing-with-es6-generators-to-make-a-maybe-in-javascript] [^http://devsmash.com/blog/whats-the-big-deal-with-generators] [^http://luisvega.me/understanding-node-generators] [^http://webreflection.blogspot.com/2013/06/on-harmony-javascript-generators.html] [^http://blog.alexmaccaw.com/how-yield-will-transform-node] [^http://tobyho.com/2013/06/16/what-are-generators/] about JavaScript generators. I have read them all and nonetheless I have struggled to understand the execution order and what are the use cases. I have summarized the learning process that got me to understanding ES6 generators.
2 |
3 | ## Building an Iterator from a Generator
4 |
5 | ```js
6 | // tonic ^6.0.0
7 | const generatorFunction = function* () {};
8 | const iterator = generatorFunction();
9 |
10 | console.log(iterator[Symbol.iterator]);
11 |
12 | // function [Symbol.iterator]()
13 | ```
14 |
15 | `generatorFunction` variable is assigned a generator function. Generator functions are denoted using `function*` syntax.
16 |
17 | Calling a generator function returns an iterator object.
18 |
19 | ```js
20 | // tonic ^6.0.0
21 | const generatorFunction = function* () {
22 | // This does not get executed.
23 | console.log('a');
24 | };
25 |
26 | console.log(1);
27 | const iterator = generatorFunction();
28 | console.log(2);
29 |
30 | // 1
31 | // 2
32 | ```
33 |
34 | ## Advancing the Generator
35 |
36 | `next()` method is used to advance the execution of the generator body:
37 |
38 | ```js
39 | // tonic ^6.0.0
40 | const generatorFunction = function* () {
41 | console.log('a');
42 | };
43 |
44 | console.log(1);
45 | const iterator = generatorFunction();
46 | console.log(2);
47 | iterator.next();
48 | console.log(3);
49 |
50 | // 1
51 | // 2
52 | // a
53 | // 3
54 | ```
55 |
56 | `next()` method returns an object that indicates the progress of the iteration:
57 |
58 | ```js
59 | // tonic ^6.0.0
60 | const generatorFunction = function* () {};
61 | const iterator = generatorFunction();
62 |
63 | console.log(iterator.next());
64 |
65 | // Object {value: undefined, done: true}
66 | ```
67 |
68 | `done` property indicates that the generator body has been run to the completion.
69 |
70 | The generator function is expected to utilize `yield` keyword. `yield` suspends execution of a generator and returns control to the iterator.
71 |
72 | ```js
73 | // tonic ^6.0.0
74 | const generatorFunction = function* () {
75 | yield;
76 | };
77 | const iterator = generatorFunction();
78 |
79 | console.log(iterator.next());
80 | console.log(iterator.next());
81 |
82 | // Object {value: undefined, done: false}
83 | // Object {value: undefined, done: true}
84 | ```
85 |
86 | When suspended, the generator does not block the event queue:
87 |
88 | ```js
89 | // tonic ^6.0.0
90 | const generatorFunction = function* () {
91 | var i = 0;
92 | while (true) {
93 | yield i++;
94 | }
95 | };
96 |
97 | const iterator = generatorFunction();
98 |
99 | console.log(iterator.next());
100 | console.log(iterator.next());
101 | console.log(iterator.next());
102 | console.log(iterator.next());
103 | console.log(iterator.next());
104 | console.log(iterator.next());
105 |
106 | // Object {value: 0, done: false}
107 | // Object {value: 1, done: false}
108 | // Object {value: 2, done: false}
109 | // Object {value: 3, done: false}
110 | // Object {value: 4, done: false}
111 | // Object {value: 5, done: false}
112 | ```
113 |
114 | ## Pass a Value To the Iterator
115 |
116 | `yield` keyword can pass a value back to the iterator:
117 |
118 | ```js
119 | // tonic ^6.0.0
120 | const generatorFunction = function* () {
121 | yield 'foo';
122 | };
123 |
124 | iterator = generatorFunction();
125 |
126 | console.log(iterator.next());
127 | console.log(iterator.next());
128 |
129 | // Object {value: "foo", done: false}
130 | // Object {value: undefined, done: true}
131 | ```
132 |
133 | Any data type can be yielded, including functions, numbers, arrays and objects.
134 |
135 | When the generator is advanced to the completion, the `return` value is returned.
136 |
137 | ```js
138 | // tonic ^6.0.0
139 | const generatorFunction = function* () {
140 | yield 'foo';
141 | return 'bar';
142 | };
143 |
144 | const iterator = generatorFunction();
145 |
146 | console.log(iterator.next());
147 | console.log(iterator.next());
148 |
149 | // Object {value: "foo", done: false}
150 | // Object {value: "bar", done: true}
151 | ```
152 |
153 | ## Receive a Value From the Iterator
154 |
155 | `yield` keyword can receive a value back from the iterator:
156 |
157 | ```js
158 | // tonic ^6.0.0
159 | const generatorFunction = function* () {
160 | console.log(yield);
161 | };
162 |
163 | const iterator = generatorFunction();
164 |
165 | iterator.next('foo');
166 | iterator.next('bar');
167 |
168 | // bar
169 | ```
170 |
171 | There is no `yield` expression to receive the first value "foo". The value is tossed-away.
172 |
173 | ## Understanding the Execution Flow
174 |
175 | The best way to understand the execution flow of the generators is to play around using a `debugger`. I have illustrated the example that I have used to wrap my head around the I/O order.
176 |
177 |
178 |
179 | Animated execution flow of the ES6 generators.
180 |
181 |
182 | ## Iterating Using the `for...of` Statement
183 |
184 | The iterator object returned from the generator is compliant with the ["iterable"](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/iterable) protocol. Therefore, you can use the [`for...of` statement](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of) to loop through the generator.
185 |
186 | ```js
187 | // tonic ^6.0.0
188 | let index;
189 |
190 | const generatorFunction = function* () {
191 | yield 1;
192 | yield 2;
193 | yield 3;
194 | return 4;
195 | };
196 |
197 | const iterator = generatorFunction();
198 |
199 | for (index of iterator) {
200 | console.log(index);
201 | }
202 |
203 | // 1
204 | // 2
205 | // 3
206 | ```
207 |
208 | * The iteration will continue as long as `done` property is `false`.
209 | * The `for..of` loop cannot be used in cases where you need to pass in values to the generator steps.
210 | * The `for..of` loop will throw away the `return` value.
211 |
212 | ## Delegating `yield`
213 |
214 | The `yield*`operator delegates to another generator.
215 |
216 | ```js
217 | // tonic ^6.0.0
218 | let index;
219 |
220 | const foo = function* () {
221 | yield 'foo';
222 | yield * bar();
223 | };
224 |
225 | const bar = function* () {
226 | yield 'bar';
227 | yield * baz();
228 | };
229 |
230 | const baz = function* () {
231 | yield 'baz';
232 | };
233 |
234 | for (index of foo()) {
235 | console.log(index);
236 | }
237 |
238 | // foo
239 | // bar
240 | // baz
241 | ```
242 |
243 | Delegating a generator to another generator is in effect the same as importing the body of the target generator to the destination generator. For illustration purposes only, the above code unfolds to the following:
244 |
245 | ```js
246 | // tonic ^6.0.0
247 | let index;
248 |
249 | const foo = function* () {
250 | yield 'foo';
251 | yield 'bar';
252 | yield 'baz';
253 | };
254 |
255 | for (index of foo()) {
256 | console.log(index);
257 | }
258 |
259 | // foo
260 | // bar
261 | // baz
262 | ```
263 |
264 | ## Throw
265 |
266 | In addition to advancing the generator instance using `next()`, you can `throw()`. Whatever is thrown will propagate back up into the code of the generator, i.e. it can be handled either within or outside the generator instance:
267 |
268 | ```js
269 | // tonic ^6.0.0
270 | const generatorFunction = function* () {
271 | while (true) {
272 | try {
273 | yield;
274 | } catch (e) {
275 | if (e != 'a') {
276 | throw e;
277 | }
278 | console.log('Generator caught', e);
279 | }
280 | }
281 | };
282 |
283 | const iterator = generatorFunction();
284 |
285 | iterator.next();
286 |
287 | try {
288 | iterator.throw('a');
289 | iterator.throw('b');
290 | } catch (e) {
291 | console.log('Uncaught', e);
292 | }
293 |
294 | // Generator caught a
295 | // Uncaught b
296 | ```
297 |
298 | Any data type can be thrown, including functions, numbers, arrays and objects.
299 |
300 | ## What Problem Do Generators Solve?
301 |
302 | In JavaScript, IO operations are generally done as asynchronous operations that require a callback. For the purpose of illustration, I am going to use a made-up service `foo`:
303 |
304 | ```js
305 | // tonic ^6.0.0
306 | const foo = (name, callback) => {
307 | setTimeout(() => {
308 | callback(name);
309 | }, 100);
310 | };
311 | ```
312 |
313 | Multiple asynchronous operations one after another produce nesting that is hard to read.
314 |
315 | ```js
316 | // tonic ^6.0.0
317 | const foo = (name, callback) => {
318 | setTimeout(() => {
319 | callback(name);
320 | }, 100);
321 | };
322 |
323 | foo('a', (a) => {
324 | foo('b', (b) => {
325 | foo('c', (c) => {
326 | console.log(a, b, c);
327 | });
328 | });
329 | });
330 |
331 | // a
332 | // b
333 | // c
334 | ```
335 |
336 | There are several solutions to address the issue, such as [using promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) or generators. Using generators, the above code can be rewritten as such:
337 |
338 | ```js
339 | // tonic ^6.0.0
340 | (function* () {
341 | const a = yield curry(foo, 'a');
342 | const b = yield curry(foo, 'b');
343 | const c = yield curry(foo, 'c');
344 |
345 | console.log(a, b, c);
346 | });
347 | ```
348 |
349 | To execute the generator, we need a controller. The controller needs to fulfill the asynchronous requests and return the result back.
350 |
351 | ```js
352 | // tonic ^6.0.0
353 | /**
354 | * Initiates a generator and iterates through each function supplied
355 | * via the yield operator.
356 | *
357 | * @param {Function}
358 | */
359 | const controller = (generator) => {
360 | const iterator = generator();
361 |
362 | const advancer = (response) => {
363 | // Advance the iterator using the response of an asynchronous callback.
364 | const state = iterator.next(response);
365 |
366 | if (!state.done) {
367 | // Make the asynchronous function call the advancer.
368 | state.value(advancer);
369 | }
370 | }
371 |
372 | advancer();
373 | };
374 | ```
375 |
376 | The last step is to curry the asynchronous functions into functions that take a single parameter (the callback). This allows to iterate the generator instance knowing that `yield` expression is always expecting a single parameter, the callback that is used to further advance the iteration.
377 |
378 | ```js
379 | // tonic ^6.0.0
380 | /**
381 | * Transforms a function that takes multiple arguments into a
382 | * function that takes just the last argument of the original function.
383 | *
384 | * @param {Function}
385 | * @param {...*}
386 | */
387 | const curry = (method, ...args) => {
388 | return (callback) => {
389 | args.push(callback);
390 |
391 | return method.apply({}, args);
392 | };
393 | };
394 | ```
395 |
396 | The end result is a script without too many levels of nested callbacks and achieved line independence (the code for one operation is no longer tied to the ones that come after it).
397 |
398 | ```js
399 | // tonic ^6.0.0
400 | const foo = (name, callback) => {
401 | setTimeout(() => {
402 | callback(name);
403 | }, 100);
404 | };
405 |
406 | const curry = (method, ...args) => {
407 | return (callback) => {
408 | args.push(callback);
409 |
410 | return method.apply({}, args);
411 | };
412 | };
413 |
414 | const controller = (generator) => {
415 | const iterator = generator();
416 |
417 | const advancer = (response) => {
418 | var state;
419 |
420 | state = iterator.next(response);
421 |
422 | if (!state.done) {
423 | state.value(advancer);
424 | }
425 | }
426 |
427 | advancer();
428 | };
429 |
430 | controller(function* () {
431 | const a = yield curry(foo, 'a');
432 | const b = yield curry(foo, 'b');
433 | const c = yield curry(foo, 'c');
434 |
435 | console.log(a, b, c);
436 | });
437 |
438 | // a
439 | // b
440 | // c
441 | ```
442 |
443 | ## Error Handling
444 |
445 | It is common to handle the error handling for each individual asynchronous operation, e.g.
446 |
447 | ```js
448 | // tonic ^6.0.0
449 | const foo = (name, callback) => {
450 | callback(null, name);
451 | };
452 |
453 | foo('a', (error1, result1) => {
454 | if (error1) {
455 | throw new Error(error1);
456 | }
457 |
458 | foo('b', (error2, result2) => {
459 | if (error2) {
460 | throw new Error(error2);
461 | }
462 |
463 | foo('c', (error3, result3) => {
464 | if (error3) {
465 | throw new Error(error3);
466 | }
467 |
468 | console.log(result1, result2, result3);
469 | });
470 | });
471 | });
472 |
473 | // a
474 | // b
475 | // c
476 | ```
477 |
478 | In the following example, I enable the controller to throw an error and use `try...catch` block to capture all errors.
479 |
480 | ```js
481 | // tonic ^6.0.0
482 | const foo = (parameters, callback) => {
483 | setTimeout(() => {
484 | callback(parameters);
485 | }, 100);
486 | };
487 |
488 | const curry = (method, ...args) => {
489 | return (callback) => {
490 | args.push(callback);
491 |
492 | return method.apply({}, args);
493 | };
494 | };
495 |
496 | const controller = (generator) => {
497 | const iterator = generator();
498 |
499 | const advancer = (response) => {
500 | if (response && response.error) {
501 | return iterator.throw(response.error);
502 | }
503 |
504 | const state = iterator.next(response);
505 |
506 | if (!state.done) {
507 | state.value(advancer);
508 | }
509 | }
510 |
511 | advancer();
512 | };
513 |
514 | controller(function* () {
515 | let a,
516 | b,
517 | c;
518 |
519 | try {
520 | a = yield curry(foo, 'a');
521 | b = yield curry(foo, {error: 'Something went wrong.'});
522 | c = yield curry(foo, 'c');
523 | } catch (e) {
524 | console.log(e);
525 | }
526 |
527 | console.log(a, b, c);
528 | });
529 |
530 | // Something went wrong.
531 | // a undefined undefined
532 |
533 | ```
534 |
535 | Notice that the execution was interrupted before `curry(foo, 'c')` was called.
536 |
537 | ## Libraries To Streamline Generator Based Flow-Control
538 |
539 | There are several existing libraries that implement a variation of the above controller, as well as offer interoperability with promises, trunks and other techniques.
540 |
541 | ## Further Reading
542 |
543 | [Exploring ES6](http://exploringjs.com/) has a chapter about [Generators](http://exploringjs.com/es6/ch_generators.html). [Axel Rauschmayer](https://twitter.com/rauschma) [write up](http://2ality.com/2015/03/es6-generators.html) about generators covers a lot more than I managed to cover in this article. It is a lengthy read, though I thoroughly recommend it.
544 |
545 | * https://github.com/jmar777/suspend
546 | * https://github.com/visionmedia/co
547 | * https://github.com/bjouhier/galaxy
548 | * https://github.com/spion/genny
549 | * https://github.com/creationix/gen-run
550 |
--------------------------------------------------------------------------------
/posts/using-dataloader-to-batch-requests/index.md:
--------------------------------------------------------------------------------
1 | Facebook [DataLoader](https://github.com/facebook/dataloader) is a generic utility used to abstract request batching and caching.
2 |
3 | I promise you – this thing is magic.
4 |
5 | I have discovered DataLoader as part of my research to solve N+1 problem arising when using GraphQL. DataLoader is referred to as a solution to reduce the number of round-trips. However, I have read the documentation and it wasn't immediately clear what it does or how it works; at first sight, it appeared to be a simple key-value cache storage with a getter function.
6 |
7 | In this post I illustrate how DataLoader achieves request batching.
8 |
9 | First of all, lets create an example of the N+1 problem.
10 |
11 | ## N+1 problem
12 |
13 | The N+1 problem arises when you have a data collection and each child of that collection owns another collection, e.g. a list of posts and each post has a list of tags.
14 |
15 | A naive object-relational mapping (ORM) implementation will first fetch the posts and then will make a query for each post to get the tags. Using an example of MySQL, that would result in 5 queries:
16 |
17 | ```sql
18 | mysql> SELECT * FROM `post`;
19 | +----+------+
20 | | id | name |
21 | +----+------+
22 | | 1 | huhu |
23 | | 2 | lala |
24 | | 3 | keke |
25 | | 4 | koko |
26 | +----+------+
27 | 4 rows in set (0.00 sec)
28 |
29 | mysql> SELECT `tag`.*, `post_tag`.`post_id` FROM `post_tag` INNER JOIN `tag` ON `tag`.`id` = `post_tag`.`tag_id` WHERE `post_tag`.`post_id` = 1;
30 | +----+------+---------+
31 | | id | name | post_id |
32 | +----+------+---------+
33 | | 1 | toto | 1 |
34 | | 2 | titi | 1 |
35 | +----+------+---------+
36 | 2 rows in set (0.00 sec)
37 |
38 | mysql> SELECT `tag`.*, `post_tag`.`post_id` FROM `post_tag` INNER JOIN `tag` ON `tag`.`id` = `post_tag`.`tag_id` WHERE `post_tag`.`post_id` = 2;
39 | +----+------+---------+
40 | | id | name | post_id |
41 | +----+------+---------+
42 | | 1 | toto | 2 |
43 | | 1 | toto | 2 |
44 | +----+------+---------+
45 | 2 rows in set (0.00 sec)
46 |
47 | mysql> SELECT `tag`.*, `post_tag`.`post_id` FROM `post_tag` INNER JOIN `tag` ON `tag`.`id` = `post_tag`.`tag_id` WHERE `post_tag`.`post_id` = 3;
48 | +----+------+---------+
49 | | id | name | post_id |
50 | +----+------+---------+
51 | | 4 | tutu | 3 |
52 | +----+------+---------+
53 | 1 row in set (0.00 sec)
54 |
55 | mysql> SELECT `tag`.*, `post_tag`.`post_id` FROM `post_tag` INNER JOIN `tag` ON `tag`.`id` = `post_tag`.`tag_id` WHERE `post_tag`.`post_id` = 4;
56 | +----+------+---------+
57 | | id | name | post_id |
58 | +----+------+---------+
59 | | 1 | toto | 4 |
60 | | 2 | titi | 4 |
61 | +----+------+---------+
62 | 2 rows in set (0.00 sec)
63 | ```
64 |
65 | ## ORMs are smart
66 |
67 | A lot of the ORMs optimize queries to fetch collection data. Here is an example using [Bookshelf](http://bookshelfjs.org/) ORM to fetch the data from the previous example:
68 |
69 | ```js
70 | import createKnex from 'knex';
71 | import createBookshelf from 'bookshelf';
72 |
73 | const knex = createKnex({
74 | client: 'mysql',
75 | connection: {
76 | host: '127.0.0.1',
77 | user: 'root',
78 | database: 'blog'
79 | }
80 | });
81 |
82 | const bookshelf = createBookshelf(knex);
83 |
84 | const Post = bookshelf.Model.extend({
85 | tableName: 'post',
86 | tags: function () {
87 | return this.belongsToMany(Tag)
88 | }
89 | });
90 |
91 | const Tag = bookshelf.Model.extend({
92 | tableName: 'tag'
93 | });
94 |
95 | bookshelf
96 | .Collection
97 | .extend({
98 | model: Post
99 | })
100 | .forge()
101 | .fetch({
102 | withRelated: 'tags'
103 | });
104 | ```
105 |
106 | The latter will fetch data using two queries:
107 |
108 | ```sql
109 | select
110 | `post`.*
111 | from
112 | `post`;
113 |
114 | select
115 | `tag`.*,
116 | `post_tag`.`post_id` as `_pivot_post_id`,
117 | `post_tag`.`tag_id` as `_pivot_tag_id`
118 | from
119 | `tag`
120 | inner join
121 | `post_tag`
122 | on
123 | `post_tag`.`tag_id` = `tag`.`id`
124 | where
125 | `post_tag`.`post_id` in (?, ?, ?, ?);
126 | ```
127 |
128 | The problem arises when you know only the parent node when requesting its dependencies. You will observe this patten in a system where each node is responsible for fetching its own dependencies, e.g. GraphQL.
129 |
130 | ## GraphQL nested queries
131 |
132 | One of the key aspects of [GraphQL](http://graphql.org/) is its ability to nest queries.
133 |
134 | To continue with the blog example, lets implement a schema that would enable us to fetch all posts and their tags, i.e. a schema that supports the following query:
135 |
136 | ```graphql
137 | {
138 | posts {
139 | id,
140 | name,
141 | tags {
142 | id,
143 | name
144 | }
145 | }
146 | }
147 | ```
148 |
149 | The schema implementation:
150 |
151 | ```js
152 | import {
153 | graphql,
154 | GraphQLID,
155 | GraphQLInt,
156 | GraphQLList,
157 | GraphQLNonNull,
158 | GraphQLObjectType,
159 | GraphQLSchema,
160 | GraphQLString
161 | } from 'graphql';
162 |
163 | // For definition of Post and Tag, refer
164 | // to the previous examples in the article.
165 | import {
166 | Post,
167 | Tag
168 | } from './models';
169 |
170 | const TagType = new GraphQLObjectType({
171 | name: 'Tag',
172 | fields: () => {
173 | return {
174 | id: {
175 | type: new GraphQLNonNull(GraphQLID)
176 | },
177 | name: {
178 | type: new GraphQLNonNull(GraphQLString)
179 | }
180 | };
181 | }
182 | });
183 |
184 | const PostType = new GraphQLObjectType({
185 | name: 'Post',
186 | fields: () => {
187 | return {
188 | id: {
189 | type: new GraphQLNonNull(GraphQLID)
190 | },
191 | name: {
192 | type: GraphQLString
193 | },
194 | tags: {
195 | type: new GraphQLList(TagType),
196 | resolve: (post) => {
197 | return Post
198 | .forge({
199 | id: post.id
200 | })
201 | .load('tags')
202 | .call('related', 'tags')
203 | .call('toJSON');
204 | }
205 | }
206 | };
207 | }
208 | });
209 |
210 | const QueryType = new GraphQLObjectType({
211 | name: 'Query',
212 | fields: {
213 | posts: {
214 | type: new GraphQLList(PostType),
215 | resolve: (root) => {
216 | return Post
217 | .fetchAll()
218 | .call('toJSON');
219 | }
220 | }
221 | }
222 | });
223 |
224 | const schema = new GraphQLSchema({
225 | query: QueryType
226 | });
227 | ```
228 |
229 | This example assumes that you have at least minimal knowledge of GraphQL. If you are not familiar with GraphQL, then a simple takeaway is:
230 |
231 | * `posts` node implements `resolve` method that fetches data for all `Post` models.
232 | * `post` has a property `tags`. `tags` `resolve` method has access to the data about the post.
233 | * `post` has a property `tags`. `tags` `resolve` method fetches data for all `Tag` models using the post `id` value.
234 |
235 | In this example, `tags` method only knows about a single post at the time of being called, i.e. it does not know whether you are fetching information about a single post or many posts. As a result, executing the earlier query will result in N+1 problem, e.g.
236 |
237 | ```js
238 | const query = `{
239 | posts {
240 | id,
241 | name,
242 | tags {
243 | id,
244 | name
245 | }
246 | }
247 | }`;
248 |
249 | graphql(schema, query)
250 | ```
251 |
252 | The latter produces the following queries:
253 |
254 | ```sql
255 | select `post`.* from `post`;
256 | select `tag`.*, `post_tag`.`post_id` as `_pivot_post_id`, `post_tag`.`tag_id` as `_pivot_tag_id` from `tag` inner join `post_tag` on `post_tag`.`tag_id` = `tag`.`id` where `post_tag`.`post_id` in (1);
257 | select `tag`.*, `post_tag`.`post_id` as `_pivot_post_id`, `post_tag`.`tag_id` as `_pivot_tag_id` from `tag` inner join `post_tag` on `post_tag`.`tag_id` = `tag`.`id` where `post_tag`.`post_id` in (2);
258 | select `tag`.*, `post_tag`.`post_id` as `_pivot_post_id`, `post_tag`.`tag_id` as `_pivot_tag_id` from `tag` inner join `post_tag` on `post_tag`.`tag_id` = `tag`.`id` where `post_tag`.`post_id` in (3);
259 | select `tag`.*, `post_tag`.`post_id` as `_pivot_post_id`, `post_tag`.`tag_id` as `_pivot_tag_id` from `tag` inner join `post_tag` on `post_tag`.`tag_id` = `tag`.`id` where `post_tag`.`post_id` in (4);
260 | ```
261 |
262 | ## Using DataLoader to batch queries
263 |
264 | [DataLoader](https://github.com/facebook/dataloader) is used to create a data loader. `DataLoader` is constructed using a batch loading function. A batch loading function accepts an array of keys, and returns a promise which resolves to an array of values.
265 |
266 | Use the resulting data loader function to load values. DataLoader will coalesce all individual loads which occur within a single tick of an event loop and then call your batch loading function.
267 |
268 | This definition of the DataLoader is taken more or less verbatim from the [documentation](https://github.com/facebook/dataloader#getting-started). It sounds cool, but it didn't make much sense to me.
269 |
270 | Lets use DataLoader to fix the N+1 problem in the blog example.
271 |
272 | First, I need a function that can load a batch of tags in one query.
273 |
274 | ```js
275 | const getPostTagsUsingPostId = (postIds) => {
276 | return Post
277 | .collection(postIds.map((id) => {
278 | return {
279 | id
280 | };
281 | }))
282 | .load('tags')
283 | .call('toJSON')
284 | .then((collection) => {
285 | // Bookshelf 0.10.0 uses Bluebird ^2.9.4.
286 | // Support for .mapSeries has been added in Bluebird v3.
287 | return collection.map((post) => {
288 | return post.tags;
289 | });
290 | });
291 | };
292 | ```
293 |
294 | `getPostTagsUsingPostId` will construct a single query to fetch tags for a list of post IDs, e.g.
295 |
296 | ```sql
297 | select
298 | `tag`.*,
299 | `post_tag`.`post_id` as `_pivot_post_id`,
300 | `post_tag`.`tag_id` as `_pivot_tag_id`
301 | from
302 | `tag`
303 | inner join
304 | `post_tag`
305 | on
306 | `post_tag`.`tag_id` = `tag`.`id`
307 | where
308 | `post_tag`.`post_id` in (?)
309 | ```
310 |
311 | Second, I need to create a data loader function:
312 |
313 | ```js
314 | import DataLoader from 'dataloader';
315 |
316 | const TagByPostIdLoader = new DataLoader(getPostTagsUsingPostId);
317 | ```
318 |
319 | Finally, I need `PostType` to use the [`.load()`](https://github.com/facebook/dataloader#loadkey) function of the resulting data loader object to resolve data:
320 |
321 | ```js
322 | const PostType = new GraphQLObjectType({
323 | name: 'Post',
324 | fields: () => {
325 | return {
326 | id: {
327 | type: new GraphQLNonNull(GraphQLID)
328 | },
329 | name: {
330 | type: GraphQLString
331 | },
332 | tags: {
333 | type: new GraphQLList(TagType),
334 | resolve: (post) => {
335 | return TagByPostIdLoader.load(post.id);
336 | }
337 | }
338 | };
339 | }
340 | });
341 | ```
342 |
343 | Now, lets rerun the earlier query:
344 |
345 | ```js
346 | const query = `{
347 | posts {
348 | id,
349 | name,
350 | tags {
351 | id,
352 | name
353 | }
354 | }
355 | }`;
356 |
357 | graphql(schema, query)
358 | ```
359 |
360 | This time, we have fetched the data using just two queries:
361 |
362 | ```sql
363 | select `post`.* from `post`
364 | select `tag`.*, `post_tag`.`post_id` as `_pivot_post_id`, `post_tag`.`tag_id` as `_pivot_tag_id` from `tag` inner join `post_tag` on `post_tag`.`tag_id` = `tag`.`id` where `post_tag`.`post_id` in (1, 2, 3, 4)
365 | ```
366 |
367 | ### How does it work?
368 |
369 | Hopefully, now the earlier description makes more sense:
370 |
371 | > DataLoader is used to create a data loader. `DataLoader` is constructed using a batch loading function. A batch loading function accepts an array of keys, and returns a promise which resolves to an array of values.
372 | >
373 | > Use the resulting data loader function to load values. DataLoader will coalesce all individual loads which occur within a single tick of an event loop and then call your batch loading function.
374 |
375 | If you are still struggling, I have made an interactive example:
376 |
377 | ```js
378 | // tonic ^6.0.0
379 | const DataLoader = require('dataloader');
380 |
381 | const getPostTagsUsingPostId = (ids) => {
382 | console.log(ids);
383 |
384 | return Promise.resolve(ids);
385 | };
386 |
387 | const TagByPostIdLoader = new DataLoader(getPostTagsUsingPostId);
388 |
389 | TagByPostIdLoader.load(1);
390 | TagByPostIdLoader.load(2);
391 | TagByPostIdLoader.load(3);
392 |
393 | // Force next-tick
394 | setTimeout(() => {
395 | TagByPostIdLoader.load(4);
396 | TagByPostIdLoader.load(5);
397 | TagByPostIdLoader.load(6);
398 | }, 100);
399 |
400 | // Force next-tick
401 | setTimeout(() => {
402 | TagByPostIdLoader.load(7);
403 | TagByPostIdLoader.load(8);
404 | TagByPostIdLoader.load(9);
405 | }, 200);
406 |
407 | setTimeout(() => {
408 | TagByPostIdLoader.load(10);
409 | TagByPostIdLoader.load(11);
410 | TagByPostIdLoader.load(12);
411 | }, 200);
412 | ```
413 |
414 | ## To sum up
415 |
416 | [DataLoader](https://github.com/facebook/dataloader) allows you to decouple unrelated parts of your application without sacrificing the performance of batch data-loading. While the loader presents an API that loads individual values, all concurrent requests will be coalesced and presented to your batch loading function. This allows your application to safely distribute data fetching requirements throughout your application and maintain minimal outgoing data requests.[^https://github.com/facebook/dataloader/blame/68a2a2e9a347ff2acc35244ae29995ab625b2075/README.md#L88]
417 |
418 | I told you – it is magic.
419 |
420 | ## Further reading
421 |
422 | I thoroughly recommend reading the [source code of the DataLoader](https://github.com/facebook/dataloader/blob/master/src/index.js) (less than 300 lines of code). Ignoring the cache logic, the [underlying implementation](https://github.com/facebook/dataloader/blob/68a2a2e9a347ff2acc35244ae29995ab625b2075/src/index.js#L206-L211) is a simple queue that is using [`process.nextTick`](https://nodejs.org/api/process.html#process_process_nexttick_callback_arg) to resolve an array of promises. Yet, it is a genius application.
423 |
424 | Finally, know that each `DataLoader` instance represents a unique cache. After being loaded once, the resulting value is cached, eliminating the redundant requests. You can leverage this to create a cache persisting throughout the life-time of the application, or create a new instance per each request. Continue to read about [DataLoader caching](https://github.com/facebook/dataloader#caching) to learn about cache invalidation.
425 |
--------------------------------------------------------------------------------
/posts/using-mysql-in-node-js/index.md:
--------------------------------------------------------------------------------
1 | The purpose of this article is to introduce the practices that I have adopted over the years of using Node.js and MySQL.
2 |
3 | For all examples, I am using [`mysql`](https://github.com/felixge/node-mysql) package.
4 |
5 | ## Promisify MySQL
6 |
7 | I am using [`Promise.promisifyAll`](http://bluebirdjs.com/docs/api/promise.promisifyall.html) to create an async equivalent of each function declared in a prototype of `mysql/lib/Connection` and `mysql/lib/Pool`.
8 |
9 | ```js
10 | const mysql = require('mysql');
11 | const Connection = require('mysql/lib/Connection');
12 | const Pool = require('mysql/lib/Pool');
13 | const Promise = require('bluebird');
14 |
15 | Promise.promisifyAll([
16 | Connection,
17 | Pool
18 | ]);
19 | ```
20 |
21 | `Promise.promisifyAll` does not modify the existing methods. For every method in the target function prototype, `Promise.promisifyAll` creates a new method affixed with `Async` ending, e.g. [`Connection.prototype.query`](https://github.com/felixge/node-mysql/blob/1720920f7afc660d37430c35c7128b20f77735e3/lib/Connection.js#L189) method remains as it is. A new method `Connection.prototype.queryAsync` is added to the `Connection.prototype`. Invoking `Connection.prototype.queryAsync` will return a promise whose fate is decided by the callback behavior of the `Connection.prototype.query` function.
22 |
23 | ## Creating a Connection
24 |
25 | It is [recommended](https://github.com/felixge/node-mysql/tree/1720920f7afc660d37430c35c7128b20f77735e3#establishing-connections) to establish an explicit connection, e.g.
26 |
27 | ```js
28 | const connection = mysql
29 | .createConnection({
30 | host: '127.0.0.1'
31 | });
32 |
33 | connection
34 | .connect((error) => {
35 | if (error) {
36 | console.error('Connection error.', error);
37 |
38 | return;
39 | }
40 |
41 | console.log('Established connection.', connection.threadId);
42 | });
43 | ```
44 |
45 | Unlike implicit connection, using `connection.connect` allows to catch connection errors early in the program execution.
46 |
47 | Since we have promisified `Connection.prototype`, we can use `connection.connectAsync` method to handle connection as a promise:
48 |
49 | ```js
50 | connection
51 | .connectAsync()
52 | .then((result) => {
53 | console.log('Established connection.', connection.threadId);
54 | })
55 | .catch((error) => {
56 | console.error('Connection error.', error);
57 | });
58 | ```
59 |
60 | ### Creating a Connection for an Operation
61 |
62 | If your program does most of the work without involving the database, then it is reasonable to open the database connection only when program logic requires it. The ideal scenario is: open a connection for a series of database tasks and close the connection when all tasks are completed.
63 |
64 | Meet [`Promise.using`](http://bluebirdjs.com/docs/api/promise.using.html):
65 |
66 | > In conjunction with [`.disposer`](http://bluebirdjs.com/docs/api/disposer.html), `using` will make sure that no matter what, the specified disposer will be called when the promise returned by the callback passed to `using` has settled. The disposer is necessary because there is no standard interface in node for disposing resources.
67 |
68 |
69 | ```js
70 | const createConnection = (connectionOptions) => {
71 | const connection = mysql.createConnection(connectionOptions);
72 |
73 | return connection
74 | .connectAsync()
75 | .then((result) => {
76 | return connection;
77 | })
78 | .disposer(() => {
79 | return connection.endAsync();
80 | });
81 | };
82 | ```
83 |
84 | Keep in mind that `connect` resolves with a value of connection status, e.g. if connection is successful, `result` is equivalent to:
85 |
86 | ```js
87 | OkPacket {
88 | fieldCount: 0,
89 | affectedRows: 0,
90 | insertId: 0,
91 | serverStatus: 2,
92 | warningCount: 0,
93 | message: '',
94 | protocol41: true,
95 | changedRows: 0
96 | }
97 | ```
98 |
99 | After establishing the connection, use the original `connection` object (constructed using `createConnection`) to make queries.
100 |
101 | I have created a helper function `createConnection` that (1) creates a connection using the provided `connectionOptions` object and (2) ends connection upon invocation of [`disposer`](http://bluebirdjs.com/docs/api/disposer.html) method.
102 |
103 | This is how you use `createConnection` with `Promise.using`:
104 |
105 | ```js
106 | Promise
107 | .using(createConnection({
108 | host: '127.0.0.1'
109 | }), (connection) => {
110 | return Promise
111 | .all([
112 | connection.queryAsync('SELECT 1'),
113 | connection.queryAsync('SELECT 2'),
114 | connection.queryAsync('SELECT 3'),
115 | ]);
116 | });
117 | ```
118 |
119 | The `disposer` callback is invoked as soon as all 3 queries are executed. All 3 queries share the same connection.
120 |
121 | ### Creating a Connection Pool
122 |
123 | If your program relies on constant connection to the database (e.g. an API endpoint that fetches data from a database), then it is desirable to have a [connection pool](https://en.wikipedia.org/wiki/Connection_pool):
124 |
125 | > Connection pools are used to enhance the performance of executing commands on a database. Opening and maintaining a database connection for each user, especially requests made to a dynamic database-driven website application, is costly and wastes resources. In connection pooling, after a connection is created, it is placed in the pool and it is used again so that a new connection does not have to be established. If all the connections are being used, a new connection is made and is added to the pool. Connection pooling also cuts down on the amount of time a user must wait to establish a connection to the database.
126 |
127 | Fortunately, abstracting connection pooling is similar to abstracting `mysql.createConnection`:
128 |
129 | ```js
130 | const pool = mysql
131 | .createPool({
132 | host: '127.0.0.1'
133 | });
134 |
135 | const getConnection = (pool) => {
136 | return pool
137 | .getConnectionAsync()
138 | .then((connection) => {
139 | return connection;
140 | })
141 | .disposer((connection) => {
142 | return connection.releaseAsync();
143 | });
144 | };
145 | ```
146 |
147 | Note the subtle differences:
148 |
149 | * `connection.releaseAsync` is used instead of `connection.endAsync`.
150 | * `pool.getConnectionAsync` is used instead of `connection.connectAsync`.
151 |
152 | This is how you use `getConnection` with `Promise.using`:
153 |
154 | ```js
155 | Promise
156 | .using(getConnection(pool), (connection) => {
157 | return Promise
158 | .all([
159 | connection.queryAsync('SELECT 1'),
160 | connection.queryAsync('SELECT 2'),
161 | connection.queryAsync('SELECT 3'),
162 | ]);
163 | });
164 | ```
165 |
166 | ## Leveraging Bluebird API
167 |
168 | In all of the examples, I have used `Bluebird.promisifyAll` to abstract [`mysql`](https://github.com/felixge/node-mysql) API. This means that all promises have access to the [Bluebird API](http://bluebirdjs.com/docs/api-reference.html). Keep that in mind when handling queries, e.g. use [`.spread`](http://bluebirdjs.com/docs/api/spread.html) method to get the first result from the array (`query` result is always an array):
169 |
170 | ```js
171 | connection
172 | .query('SELECT 1')
173 | .spread((id) => {
174 | assert(id === 1);
175 | });
176 | ```
177 |
178 | ## Reducing Nesting using Async Functions
179 |
180 | In general, promises are great for preventing the [callback hell](http://callbackhell.com/). However, consider an example where you have a series of queries that depend on the result of a previous query:
181 |
182 | ```js
183 | const getUserByEmail = (connection, userEmail) => {
184 | return connection
185 | .queryAsync('SELECT `id` FROM `user` WHERE `email` = ?', [
186 | userEmail
187 | ])
188 | .spread((user) => {
189 | if (!user) {
190 | return null;
191 | }
192 |
193 | return connection
194 | .queryAsync('SELECT `id`, `name` FROM `permission` WHERE `user_id` = ?', [
195 | user.id
196 | ])
197 | .then((permissions) => {
198 | return {
199 | ...user,
200 | permissions: permissions
201 | }
202 | });
203 | });
204 | };
205 | ```
206 |
207 | Using [Async Functions](https://github.com/tc39/ecmascript-asyncawait) we can flatten the dependency structure using `await` keyword, e.g.
208 |
209 | ```js
210 | const getUserByEmail = async (connection, userEmail) => {
211 | const user = await connection
212 | .queryAsync('SELECT `id` FROM `user` WHERE `email` = ?', [
213 | userEmail
214 | ])
215 | .then(_.head);
216 |
217 | if (!user) {
218 | return null;
219 | }
220 |
221 | const permissions = await connection
222 | .queryAsync('SELECT `id`, `name` FROM `permission` WHERE `user_id` = ?', [
223 | user.id
224 | ]);
225 |
226 | return {
227 | ...user,
228 | permissions: permissions
229 | };
230 | };
231 | ```
232 |
233 | Note: [`_.head`](https://lodash.com/docs#head) is a [lodash](https://lodash.com/) function.
234 |
235 | ## Named Placeholders
236 |
237 | `mysql` module uses `?` characters as placeholders for values, e.g.
238 |
239 | ```sql
240 | connection
241 | .query('SELECT ?, ?', [
242 | 'foo',
243 | 'bar'
244 | ]);
245 | ```
246 |
247 | This example is equivalent to:
248 |
249 | ```sql
250 | connection.query('SELECT ' + connection.escape('foo') + ', ' + connection.escape('bar'));
251 | ```
252 |
253 | However, this approach becomes hard to read as query becomes large and the list of values long.
254 |
255 | There is an alternative: named placeholders.
256 |
257 | ```sql
258 | connection
259 | .query('SELECT :foo, :bar', {
260 | foo: 'FOO',
261 | bar: 'BAR'
262 | });
263 | ```
264 |
265 | The latter is equivalent to:
266 |
267 | ```sql
268 | connection.query('SELECT ' + connection.escape('FOO') + ', ' + connection.escape('BAR'));
269 | ```
270 |
271 | Placeholder names can appear multiple times, e.g.
272 |
273 | ```sql
274 | connection
275 | .query('SELECT :foo, :foo', {
276 | foo: 'FOO'
277 | });
278 | ```
279 |
280 | The latter is equivalent to:
281 |
282 | ```sql
283 | connection.query('SELECT ' + connection.escape('FOO') + ', ' + connection.escape('FOO'));
284 | ```
285 |
286 | As of this writing, `mysql` [does not support named parameters](https://github.com/felixge/node-mysql/issues/920).
287 |
288 | However, it is easy to patch `Connection.prototype.query` prototype to add the support:
289 |
290 | First, you need to install [`named-placeholders`](https://www.npmjs.com/package/named-placeholders) package.
291 |
292 | Then, patch the `Connection.prototype.query`:
293 |
294 | ```js
295 | const toUnnamed = require('named-placeholders')();
296 | const originalQuery = require('mysql/lib/Connection').prototype.query;
297 |
298 | require('mysql/lib/Connection').prototype.query = function (...args) {
299 | if (Array.isArray(args[0]) || !args[1]) {
300 | return originalQuery.apply(this, args);
301 | }
302 |
303 | ([
304 | args[0],
305 | args[1]
306 | ] = toUnnamed(args[0], args[1]));
307 |
308 | return originalQuery.apply(this, args);
309 | };
310 | ```
311 |
312 | That's it. You can now use named placeholders.
313 |
314 | ## Constructing Queries
315 |
316 | There are several problems associated with constructing queries.
317 |
318 | ### Queries That Span Multiple Lines
319 |
320 | The problem with writing queries that span multiple lines is simple: JavaScript does not support strings that span multiple lines. The closest thing to multi-line string support is [template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals). The problem with using a template literal to declare a MySQL query is that their syntaxes are incompatible:
321 |
322 | * Template literals are [enclosed by the back-tick (`` ` ``)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Syntax) character; and
323 | * MySQL uses the backtick character to [quote the identifiers](http://dev.mysql.com/doc/refman/5.7/en/identifiers.html).
324 |
325 | For this reason, I store each query in a dedicated file. The added benefit of having SQL queries in a dedicated file is:
326 |
327 | * Makes it easy to track changes in the version control.
328 | * Separates code development from query writing.
329 | * Enforces statically typed queries (prohibits inline conditions).
330 | * You can load SQL queries in a dedicated IDE.
331 |
332 | Tip: To use queries defined in a separate file, create a helper function that loads the query and caches the result.
333 |
334 | ### Dynamic Expressions
335 |
336 | First of all, I avoid use of dynamic expressions whenever possible. Reason: when queries are predictable, DBAs can optimize indexes. When you need to query data based on dynamic criteria, consider using a search server (e.g. https://www.elastic.co/).
337 |
338 | However, there are cases (e.g. building a prototype product) when you will want to dynamically build queries simply because it is less complex than the alternatives.
339 |
340 | In this case, I suggest using a query builder (e.g. [Squel.js](https://hiddentao.github.io/squel)) to build expressions.
341 |
342 | Note: I do not recommend building queries using a query builder. A common pro-query builder argument is that abstracting SQL code using a query builder makes it easy to migrate from one database engine to another, e.g. from MySQL to PostgresQL. This might be the case, but you got to ask yourself – how many times in your professional career did you need to migrate a project from one database to another database without rewriting most of the underlying code regardless of whether you were using a query builder/ ORM or not. Probably not that many.
343 |
344 | Lets consider a simple example:
345 |
346 | ```sql
347 | CREATE TABLE `person` (
348 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
349 | `first_name` varchar(255) DEFAULT NULL,
350 | `last_name` varchar(255) DEFAULT NULL,
351 | `email` varchar(255) DEFAULT NULL,
352 | PRIMARY KEY (`id`),
353 | UNIQUE KEY `email` (`email`)
354 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
355 | ```
356 |
357 | A database table storing people data. The requirement: user must be able to filter data using any of the `person` defining columns.
358 |
359 | Start by writing a query to get all the relevant records:
360 |
361 | ```sql
362 | SELECT
363 | `id`,
364 | `first_name`,
365 | `last_name`,
366 | `email`
367 | FROM
368 | `person`
369 | WHERE
370 | 1 /* expression */
371 | ```
372 |
373 | `WHERE 1` forces to return all results.
374 |
375 | Now lets build an expression using [`squel.expr`](http://hiddentao.github.io/squel/#expressions) to filter only the relevant records:
376 |
377 | ```js
378 | const squel = require('squel');
379 |
380 | const expression = squel.expr();
381 |
382 | if (userQuery.firstName) {
383 | expression.and('`first_name` LIKE ?', userQuery.firstName);
384 | }
385 |
386 | if (userQuery.lastName) {
387 | expression.and('`last_name` LIKE ?', userQuery.lastName);
388 | }
389 |
390 | if (userQuery.email) {
391 | expression.and('`email` LIKE ?', userQuery.email);
392 | }
393 | ```
394 |
395 | Use `squel.expr().toString()` to convert query object to SQL expression (or an empty string if query is empty). Use `String.prototype.replace` to replace `1 /* expression */` with the generated expression or `1` (`WHERE` clause requires a specifier).
396 |
397 | ```js
398 | const fs = require('fs');
399 | const selectPersonsQuery = fs.readFileSync('./selectPersonsQuery.sql', 'utf8');
400 |
401 | const whereExpression = expression.toString();
402 |
403 | const generatedSelectPersonsQuery = selectPersonsQuery.replace('1 /* expression */', whereExpression || 1);
404 |
405 | connection.query(generatedSelectPersonsQuery);
406 | ```
407 |
408 | Done. You have a working query builder.
409 |
410 | This approach allows utilization of query builder without sacrificing the readability of the query body.
411 |
412 | ## Debugging
413 |
414 | `mysql` package documentation is a lengthy, monolithic document. As a result, it is not a surprise that the [section about debugging](https://github.com/felixge/node-mysql/tree/1720920f7afc660d37430c35c7128b20f77735e3#debugging-and-reporting-problems) is often overlooked.
415 |
416 | Debugging is enabled at the time of configuring the connection, e.g.
417 |
418 | ```js
419 | mysql
420 | .createConnection({
421 | debug: true
422 | });
423 | ```
424 |
425 | Enabling debugging will print all outgoing and incoming packets on stdout. You can filter the log by the packet type:
426 |
427 | ```js
428 | mysql
429 | .createConnection({
430 | debug: [
431 | 'ComQueryPacket',
432 | 'RowDataPacket'
433 | ]
434 | });
435 | ```
436 |
437 | This configuration will log only the queries being sent and the result being returned, e.g.
438 |
439 | ```
440 | --> ComQueryPacket
441 | ComQueryPacket { command: 3, sql: 'SELECT \'A\', \'B\'' }
442 |
443 | <-- RowDataPacket
444 | RowDataPacket { A: 'A', B: 'B' }
445 | ```
446 |
447 | ## `mysql2`
448 |
449 | [`mysql2`](https://github.com/sidorares/node-mysql2) is an alternative MySQL driver for Node.js. It is [mostly](https://github.com/sidorares/node-mysql2/tree/cd16a0a7ad2d273fa5126135479d4698bd554cea#known-incompatibilities-with-node-mysql) compatible with the `mysql` driver.
450 |
451 | Notable differences between `mysql2` and `mysql` are:
452 |
453 | * `mysql2` has a considerable performance advantage.
454 | * `mysql2` natively supports named placeholders[^https://github.com/sidorares/node-mysql2#named-placeholders].
455 | * `mysql2` supports true prepared statements[^https://github.com/sidorares/node-mysql2#prepared-statements]. `mysql` implementation imitates[^https://github.com/felixge/node-mysql/tree/1720920f7afc660d37430c35c7128b20f77735e3#escaping-query-values] prepared statements by escaping the values and executing the query with interpolated values.
456 |
457 | In my eyes, the only downside to using `mysql2` over `mysql` is its relatively small community:
458 |
459 | ||`mysql`|`mysql2`|
460 | |---|---|---|
461 | |GitHub Stargazers|6695|284|
462 | |GitHub Forks|1087|57|
463 | |Github Watchers|378|30|
464 | |NPM downloads last month|462,781|11,701|
465 |
466 | Last updated: Tue Apr 26 20:53:20 2016.
467 |
468 | ### Performance
469 |
470 | `mysql2` boosts considerable performance improvements over `mysql`. Here is a summary of the [benchmark](https://gist.github.com/sidorares/ffe9ee9c423f763e3b6b) that `mysql2` is using to prove its edge:
471 |
472 | * `node-mysql`: peaks 6000 rps, first timeout errors at 110 conns, latency99 60ms at 110 conns
473 | * `node-mysql2`: peak 15000 rps, first timeout errors at 150 conns, latency99 30ms at 150 conn
474 | * `memory`: peak 46000 rps, no erros, latency99 20ms at 300 conns (7ms at 120 conn)
475 |
--------------------------------------------------------------------------------