Adding an initial S3 sample and code to register for event notification on an S3 bucket.
Showing
56 changed files
with
2343 additions
and
2 deletions
... | @@ -170,7 +170,20 @@ class Kappa(object): | ... | @@ -170,7 +170,20 @@ class Kappa(object): |
170 | for log_event in response['events']: | 170 | for log_event in response['events']: |
171 | print(log_event['message']) | 171 | print(log_event['message']) |
172 | 172 | ||
173 | - def add_event_source(self): | 173 | + def _get_function_arn(self): |
174 | + name = self.config['lambda']['name'] | ||
175 | + arn = None | ||
176 | + lambda_svc = self.session.create_client('lambda', self.region) | ||
177 | + try: | ||
178 | + response = lambda_svc.get_function_configuration( | ||
179 | + FunctionName=name) | ||
180 | + LOG.debug(response) | ||
181 | + arn = response['FunctionARN'] | ||
182 | + except Exception: | ||
183 | + LOG.debug('Unable to find ARN for function: %s' % name) | ||
184 | + return arn | ||
185 | + | ||
186 | + def _add_kinesis_event_source(self, event_source_arn): | ||
174 | lambda_svc = self.session.create_client('lambda', self.region) | 187 | lambda_svc = self.session.create_client('lambda', self.region) |
175 | try: | 188 | try: |
176 | invoke_role = self.get_role_arn( | 189 | invoke_role = self.get_role_arn( |
... | @@ -178,12 +191,38 @@ class Kappa(object): | ... | @@ -178,12 +191,38 @@ class Kappa(object): |
178 | response = lambda_svc.add_event_source( | 191 | response = lambda_svc.add_event_source( |
179 | FunctionName=self.config['lambda']['name'], | 192 | FunctionName=self.config['lambda']['name'], |
180 | Role=invoke_role, | 193 | Role=invoke_role, |
181 | - EventSource=self.config['lambda']['event_source'], | 194 | + EventSource=event_source_arn, |
182 | BatchSize=self.config['lambda'].get('batch_size', 100)) | 195 | BatchSize=self.config['lambda'].get('batch_size', 100)) |
183 | LOG.debug(response) | 196 | LOG.debug(response) |
184 | except Exception: | 197 | except Exception: |
185 | LOG.exception('Unable to add event source') | 198 | LOG.exception('Unable to add event source') |
186 | 199 | ||
200 | + def _add_s3_event_source(self, event_source_arn): | ||
201 | + s3_svc = self.session.create_client('s3', self.region) | ||
202 | + bucket_name = event_source_arn.split(':')[-1] | ||
203 | + invoke_role = self.get_role_arn( | ||
204 | + self.config['cloudformation']['invoke_role']) | ||
205 | + notification_spec = { | ||
206 | + 'CloudFunctionConfiguration': { | ||
207 | + 'Id': 'Kappa-%s-notification' % self.config['lambda']['name'], | ||
208 | + 'Events': [e for e in self.config['lambda']['s3_events']], | ||
209 | + 'CloudFunction': self._get_function_arn(), | ||
210 | + 'InvocationRole': invoke_role}} | ||
211 | + response = s3_svc.put_bucket_notification( | ||
212 | + Bucket=bucket_name, | ||
213 | + NotificationConfiguration=notification_spec) | ||
214 | + LOG.debug(response) | ||
215 | + | ||
216 | + def add_event_source(self): | ||
217 | + event_source_arn = self.config['lambda']['event_source'] | ||
218 | + _, _, svc, _ = event_source_arn.split(':', 3) | ||
219 | + if svc == 'kinesis': | ||
220 | + self._add_kinesis_event_source(event_source_arn) | ||
221 | + elif svc == 's3': | ||
222 | + self._add_s3_event_source(event_source_arn) | ||
223 | + else: | ||
224 | + raise ValueError('Unsupported event source: %s' % event_source_arn) | ||
225 | + | ||
187 | def deploy(self): | 226 | def deploy(self): |
188 | self.create_update_roles( | 227 | self.create_update_roles( |
189 | self.config['cloudformation']['stack_name'], | 228 | self.config['cloudformation']['stack_name'], | ... | ... |
samples/s3/config.yml
0 → 100644
1 | +--- | ||
2 | +profile: personal | ||
3 | +region: us-east-1 | ||
4 | +cloudformation: | ||
5 | + template: roles.cf | ||
6 | + stack_name: TestS3 | ||
7 | + exec_role: ExecRole | ||
8 | + invoke_role: InvokeRole | ||
9 | +lambda: | ||
10 | + name: S3Sample | ||
11 | + zipfile_name: S3Sample.zip | ||
12 | + description: Testing S3 Lambda handler | ||
13 | + path: examplefolder | ||
14 | + handler: CreateThumbnail.handler | ||
15 | + runtime: nodejs | ||
16 | + memory_size: 128 | ||
17 | + timeout: 3 | ||
18 | + mode: event | ||
19 | + test_data: input.json | ||
20 | + event_source: arn:aws:s3:::garnaat_pub | ||
21 | + s3_events: | ||
22 | + - s3:ObjectCreated:* | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
samples/s3/examplefolder/CreateThumbnail.js
0 → 100644
1 | +// dependencies | ||
2 | +var async = require('async'); | ||
3 | +var AWS = require('aws-sdk'); | ||
4 | +var gm = require('gm') | ||
5 | + .subClass({ imageMagick: true }); // Enable ImageMagick integration. | ||
6 | +var util = require('util'); | ||
7 | + | ||
8 | +// constants | ||
9 | +var MAX_WIDTH = 100; | ||
10 | +var MAX_HEIGHT = 100; | ||
11 | + | ||
12 | +// get reference to S3 client | ||
13 | +var s3 = new AWS.S3(); | ||
14 | + | ||
15 | +exports.handler = function(event, context) { | ||
16 | + // Read options from the event. | ||
17 | + console.log("Reading options from event:\n", util.inspect(event, {depth: 5})); | ||
18 | + var srcBucket = event.Records[0].s3.bucket.name; | ||
19 | + var srcKey = event.Records[0].s3.object.key; | ||
20 | + var dstBucket = srcBucket + "resized"; | ||
21 | + var dstKey = "resized-" + srcKey; | ||
22 | + | ||
23 | + // Sanity check: validate that source and destination are different buckets. | ||
24 | + if (srcBucket == dstBucket) { | ||
25 | + console.error("Destination bucket must not match source bucket."); | ||
26 | + return; | ||
27 | + } | ||
28 | + | ||
29 | + // Infer the image type. | ||
30 | + var typeMatch = srcKey.match(/\.([^.]*)$/); | ||
31 | + if (!typeMatch) { | ||
32 | + console.error('unable to infer image type for key ' + srcKey); | ||
33 | + return; | ||
34 | + } | ||
35 | + var imageType = typeMatch[1]; | ||
36 | + if (imageType != "jpg" && imageType != "png") { | ||
37 | + console.log('skipping non-image ' + srcKey); | ||
38 | + return; | ||
39 | + } | ||
40 | + | ||
41 | + // Download the image from S3, transform, and upload to a different S3 bucket. | ||
42 | + async.waterfall([ | ||
43 | + function download(next) { | ||
44 | + // Download the image from S3 into a buffer. | ||
45 | + s3.getObject({ | ||
46 | + Bucket: srcBucket, | ||
47 | + Key: srcKey | ||
48 | + }, | ||
49 | + next); | ||
50 | + }, | ||
51 | + function tranform(response, next) { | ||
52 | + gm(response.Body).size(function(err, size) { | ||
53 | + // Infer the scaling factor to avoid stretching the image unnaturally. | ||
54 | + var scalingFactor = Math.min( | ||
55 | + MAX_WIDTH / size.width, | ||
56 | + MAX_HEIGHT / size.height | ||
57 | + ); | ||
58 | + var width = scalingFactor * size.width; | ||
59 | + var height = scalingFactor * size.height; | ||
60 | + | ||
61 | + // Transform the image buffer in memory. | ||
62 | + this.resize(width, height) | ||
63 | + .toBuffer(imageType, function(err, buffer) { | ||
64 | + if (err) { | ||
65 | + next(err); | ||
66 | + } else { | ||
67 | + next(null, response.ContentType, buffer); | ||
68 | + } | ||
69 | + }); | ||
70 | + }); | ||
71 | + }, | ||
72 | + function upload(contentType, data, next) { | ||
73 | + // Stream the transformed image to a different S3 bucket. | ||
74 | + s3.putObject({ | ||
75 | + Bucket: dstBucket, | ||
76 | + Key: dstKey, | ||
77 | + Body: data, | ||
78 | + ContentType: contentType | ||
79 | + }, | ||
80 | + next); | ||
81 | + } | ||
82 | + ], function (err) { | ||
83 | + if (err) { | ||
84 | + console.error( | ||
85 | + 'Unable to resize ' + srcBucket + '/' + srcKey + | ||
86 | + ' and upload to ' + dstBucket + '/' + dstKey + | ||
87 | + ' due to an error: ' + err | ||
88 | + ); | ||
89 | + } else { | ||
90 | + console.log( | ||
91 | + 'Successfully resized ' + srcBucket + '/' + srcKey + | ||
92 | + ' and uploaded to ' + dstBucket + '/' + dstKey | ||
93 | + ); | ||
94 | + } | ||
95 | + | ||
96 | + context.done(); | ||
97 | + } | ||
98 | + ); | ||
99 | +}; |
1 | +Copyright (c) 2010-2014 Caolan McMahon | ||
2 | + | ||
3 | +Permission is hereby granted, free of charge, to any person obtaining a copy | ||
4 | +of this software and associated documentation files (the "Software"), to deal | ||
5 | +in the Software without restriction, including without limitation the rights | ||
6 | +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
7 | +copies of the Software, and to permit persons to whom the Software is | ||
8 | +furnished to do so, subject to the following conditions: | ||
9 | + | ||
10 | +The above copyright notice and this permission notice shall be included in | ||
11 | +all copies or substantial portions of the Software. | ||
12 | + | ||
13 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
14 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
15 | +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
16 | +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
17 | +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
18 | +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
19 | +THE SOFTWARE. |
This diff is collapsed. Click to expand it.
1 | +{ | ||
2 | + "name": "async", | ||
3 | + "repo": "caolan/async", | ||
4 | + "description": "Higher-order functions and common patterns for asynchronous code", | ||
5 | + "version": "0.1.23", | ||
6 | + "keywords": [], | ||
7 | + "dependencies": {}, | ||
8 | + "development": {}, | ||
9 | + "main": "lib/async.js", | ||
10 | + "scripts": [ "lib/async.js" ] | ||
11 | +} |
1 | +{ | ||
2 | + "name": "async", | ||
3 | + "description": "Higher-order functions and common patterns for asynchronous code", | ||
4 | + "main": "./lib/async", | ||
5 | + "author": { | ||
6 | + "name": "Caolan McMahon" | ||
7 | + }, | ||
8 | + "version": "0.9.0", | ||
9 | + "repository": { | ||
10 | + "type": "git", | ||
11 | + "url": "https://github.com/caolan/async.git" | ||
12 | + }, | ||
13 | + "bugs": { | ||
14 | + "url": "https://github.com/caolan/async/issues" | ||
15 | + }, | ||
16 | + "licenses": [ | ||
17 | + { | ||
18 | + "type": "MIT", | ||
19 | + "url": "https://github.com/caolan/async/raw/master/LICENSE" | ||
20 | + } | ||
21 | + ], | ||
22 | + "devDependencies": { | ||
23 | + "nodeunit": ">0.0.0", | ||
24 | + "uglify-js": "1.2.x", | ||
25 | + "nodelint": ">0.0.0" | ||
26 | + }, | ||
27 | + "jam": { | ||
28 | + "main": "lib/async.js", | ||
29 | + "include": [ | ||
30 | + "lib/async.js", | ||
31 | + "README.md", | ||
32 | + "LICENSE" | ||
33 | + ] | ||
34 | + }, | ||
35 | + "scripts": { | ||
36 | + "test": "nodeunit test/test-async.js" | ||
37 | + }, | ||
38 | + "homepage": "https://github.com/caolan/async", | ||
39 | + "_id": "async@0.9.0", | ||
40 | + "dist": { | ||
41 | + "shasum": "ac3613b1da9bed1b47510bb4651b8931e47146c7", | ||
42 | + "tarball": "http://registry.npmjs.org/async/-/async-0.9.0.tgz" | ||
43 | + }, | ||
44 | + "_from": "async@", | ||
45 | + "_npmVersion": "1.4.3", | ||
46 | + "_npmUser": { | ||
47 | + "name": "caolan", | ||
48 | + "email": "caolan.mcmahon@gmail.com" | ||
49 | + }, | ||
50 | + "maintainers": [ | ||
51 | + { | ||
52 | + "name": "caolan", | ||
53 | + "email": "caolan@caolanmcmahon.com" | ||
54 | + } | ||
55 | + ], | ||
56 | + "directories": {}, | ||
57 | + "_shasum": "ac3613b1da9bed1b47510bb4651b8931e47146c7", | ||
58 | + "_resolved": "https://registry.npmjs.org/async/-/async-0.9.0.tgz", | ||
59 | + "readme": "ERROR: No README data found!" | ||
60 | +} |
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
1 | + | ||
2 | +/** | ||
3 | + * Module dependencies. | ||
4 | + */ | ||
5 | + | ||
6 | +var Stream = require('stream').Stream; | ||
7 | +var EventEmitter = require('events').EventEmitter; | ||
8 | +var util = require('util'); | ||
9 | + | ||
10 | +util.inherits(gm, EventEmitter); | ||
11 | + | ||
12 | +/** | ||
13 | + * Constructor. | ||
14 | + * | ||
15 | + * @param {String|Number} path - path to img source or ReadableStream or width of img to create | ||
16 | + * @param {Number} [height] - optional filename of ReadableStream or height of img to create | ||
17 | + * @param {String} [color] - optional hex background color of created img | ||
18 | + */ | ||
19 | + | ||
20 | +function gm (source, height, color) { | ||
21 | + var width; | ||
22 | + | ||
23 | + if (!(this instanceof gm)) { | ||
24 | + return new gm(source, height, color); | ||
25 | + } | ||
26 | + | ||
27 | + EventEmitter.call(this); | ||
28 | + | ||
29 | + this._options = {}; | ||
30 | + this.options(this.__proto__._options); | ||
31 | + | ||
32 | + this.data = {}; | ||
33 | + this._in = []; | ||
34 | + this._out = []; | ||
35 | + this._outputFormat = null; | ||
36 | + this._subCommand = 'convert'; | ||
37 | + | ||
38 | + if (source instanceof Stream) { | ||
39 | + this.sourceStream = source; | ||
40 | + source = height || 'unknown.jpg'; | ||
41 | + } else if (Buffer.isBuffer(source)) { | ||
42 | + this.sourceBuffer = source; | ||
43 | + source = height || 'unknown.jpg'; | ||
44 | + } else if (height) { | ||
45 | + // new images | ||
46 | + width = source; | ||
47 | + source = ""; | ||
48 | + | ||
49 | + this.in("-size", width + "x" + height); | ||
50 | + | ||
51 | + if (color) { | ||
52 | + this.in("xc:"+ color); | ||
53 | + } | ||
54 | + } | ||
55 | + | ||
56 | + if (typeof source === "string") { | ||
57 | + // then source is a path | ||
58 | + | ||
59 | + // parse out gif frame brackets from filename | ||
60 | + // since stream doesn't use source path | ||
61 | + // eg. "filename.gif[0]" | ||
62 | + var frames = source.match(/(\[.+\])$/); | ||
63 | + if (frames) { | ||
64 | + this.sourceFrames = source.substr(frames.index, frames[0].length); | ||
65 | + source = source.substr(0, frames.index); | ||
66 | + } | ||
67 | + } | ||
68 | + | ||
69 | + this.source = source; | ||
70 | + | ||
71 | + this.addSrcFormatter(function (src) { | ||
72 | + // must be first source formatter | ||
73 | + | ||
74 | + var inputFromStdin = this.sourceStream || this.sourceBuffer; | ||
75 | + var ret = inputFromStdin ? '-' : this.source; | ||
76 | + | ||
77 | + if (ret && this.sourceFrames) ret += this.sourceFrames; | ||
78 | + | ||
79 | + src.length = 0; | ||
80 | + src[0] = ret; | ||
81 | + }); | ||
82 | +} | ||
83 | + | ||
84 | +/** | ||
85 | + * Subclasses the gm constructor with custom options. | ||
86 | + * | ||
87 | + * @param {options} options | ||
88 | + * @return {gm} the subclasses gm constructor | ||
89 | + */ | ||
90 | + | ||
91 | +var parent = gm; | ||
92 | +gm.subClass = function subClass (options) { | ||
93 | + function gm (source, height, color) { | ||
94 | + if (!(this instanceof parent)) { | ||
95 | + return new gm(source, height, color); | ||
96 | + } | ||
97 | + | ||
98 | + parent.call(this, source, height, color); | ||
99 | + } | ||
100 | + | ||
101 | + gm.prototype.__proto__ = parent.prototype; | ||
102 | + gm.prototype._options = {}; | ||
103 | + gm.prototype.options(options); | ||
104 | + | ||
105 | + return gm; | ||
106 | +} | ||
107 | + | ||
108 | +/** | ||
109 | + * Augment the prototype. | ||
110 | + */ | ||
111 | + | ||
112 | +require("./lib/options")(gm.prototype); | ||
113 | +require("./lib/getters")(gm); | ||
114 | +require("./lib/args")(gm.prototype); | ||
115 | +require("./lib/drawing")(gm.prototype); | ||
116 | +require("./lib/convenience")(gm.prototype); | ||
117 | +require("./lib/command")(gm.prototype); | ||
118 | +require("./lib/compare")(gm.prototype); | ||
119 | +require("./lib/composite")(gm.prototype); | ||
120 | + | ||
121 | +/** | ||
122 | + * Expose. | ||
123 | + */ | ||
124 | + | ||
125 | +module.exports = exports = gm; | ||
126 | +module.exports.utils = require('./lib/utils'); | ||
127 | +module.exports.compare = require('./lib/compare')(); | ||
128 | +module.exports.version = JSON.parse( | ||
129 | + require('fs').readFileSync(__dirname + '/package.json', 'utf8') | ||
130 | +).version; | ||
131 | + |
1 | +# Compiled source # | ||
2 | +################### | ||
3 | +*.com | ||
4 | +*.class | ||
5 | +*.dll | ||
6 | +*.exe | ||
7 | +*.o | ||
8 | +*.so | ||
9 | + | ||
10 | +# Packages # | ||
11 | +############ | ||
12 | +# it's better to unpack these files and commit the raw source | ||
13 | +# git has its own built in compression methods | ||
14 | +*.7z | ||
15 | +*.dmg | ||
16 | +*.gz | ||
17 | +*.iso | ||
18 | +*.jar | ||
19 | +*.rar | ||
20 | +*.tar | ||
21 | +*.zip | ||
22 | + | ||
23 | +# Logs and databases # | ||
24 | +###################### | ||
25 | +*.log | ||
26 | +*.sql | ||
27 | +*.sqlite | ||
28 | + | ||
29 | +# OS generated files # | ||
30 | +###################### | ||
31 | +.DS_Store* | ||
32 | +ehthumbs.db | ||
33 | +Icon? | ||
34 | +Thumbs.db | ||
35 | + | ||
36 | +# Node.js # | ||
37 | +########### | ||
38 | +lib-cov | ||
39 | +*.seed | ||
40 | +*.log | ||
41 | +*.csv | ||
42 | +*.dat | ||
43 | +*.out | ||
44 | +*.pid | ||
45 | +*.gz | ||
46 | + | ||
47 | +pids | ||
48 | +logs | ||
49 | +results | ||
50 | + | ||
51 | +node_modules | ||
52 | +npm-debug.log | ||
53 | + | ||
54 | +# Components # | ||
55 | +############## | ||
56 | + | ||
57 | +/build | ||
58 | +/components | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | +# Array Series [](https://travis-ci.org/component/array-parallel) | ||
2 | + | ||
3 | +Call an array of asynchronous functions in parallel | ||
4 | + | ||
5 | +### API | ||
6 | + | ||
7 | +#### parallel(fns[, context[, callback]]) | ||
8 | + | ||
9 | +```js | ||
10 | +var parallel = require('array-parallel') | ||
11 | + | ||
12 | +parallel([ | ||
13 | + function (done) { | ||
14 | + done() | ||
15 | + } | ||
16 | +], this, function (err) { | ||
17 | + | ||
18 | +}) | ||
19 | +``` | ||
20 | + | ||
21 | +#### fns | ||
22 | + | ||
23 | +`fns` is an array of functions to call in parallel. | ||
24 | +The argument signature should be: | ||
25 | + | ||
26 | +```js | ||
27 | +function (done) { | ||
28 | + done(new Error()) | ||
29 | + // or | ||
30 | + done(null, result) | ||
31 | +} | ||
32 | +``` | ||
33 | + | ||
34 | +That is, each function should only take a `done` as an argument. | ||
35 | +Each callback should only take an `Error` as the first argument, | ||
36 | +or a value as the second. | ||
37 | + | ||
38 | +#### context | ||
39 | + | ||
40 | +Optional context to pass to each `fn`. | ||
41 | +Basically `fn.call(context, done)`. | ||
42 | + | ||
43 | +#### callback(err, results) | ||
44 | + | ||
45 | +```js | ||
46 | +function (err, results) { | ||
47 | + | ||
48 | +} | ||
49 | +``` | ||
50 | + | ||
51 | +Only argument is an `Error` argument. | ||
52 | +It will be the first error retrieved from all the `fns`. | ||
53 | +`results` will be an array of results from each `fn`, | ||
54 | +thus this could be considered an asynchronous version of `[].map`. | ||
55 | + | ||
56 | +### License | ||
57 | + | ||
58 | +The MIT License (MIT) | ||
59 | + | ||
60 | +Copyright (c) 2013 Jonathan Ong me@jongleberry.com | ||
61 | + | ||
62 | +Permission is hereby granted, free of charge, to any person obtaining a copy | ||
63 | +of this software and associated documentation files (the "Software"), to deal | ||
64 | +in the Software without restriction, including without limitation the rights | ||
65 | +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
66 | +copies of the Software, and to permit persons to whom the Software is | ||
67 | +furnished to do so, subject to the following conditions: | ||
68 | + | ||
69 | +The above copyright notice and this permission notice shall be included in | ||
70 | +all copies or substantial portions of the Software. | ||
71 | + | ||
72 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
73 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
74 | +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
75 | +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
76 | +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
77 | +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
78 | +THE SOFTWARE. |
1 | +{ | ||
2 | + "name": "array-parallel", | ||
3 | + "description": "Call an array of asynchronous functions in parallel", | ||
4 | + "repo": "array-parallel", | ||
5 | + "version": "0.1.3", | ||
6 | + "main": "index.js", | ||
7 | + "scripts": [ | ||
8 | + "index.js" | ||
9 | + ], | ||
10 | + "license": "MIT" | ||
11 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | +module.exports = function parallel(fns, context, callback) { | ||
2 | + if (!callback) { | ||
3 | + if (typeof context === 'function') { | ||
4 | + callback = context | ||
5 | + context = null | ||
6 | + } else { | ||
7 | + callback = noop | ||
8 | + } | ||
9 | + } | ||
10 | + | ||
11 | + var pending = fns && fns.length | ||
12 | + if (!pending) return callback(null, []); | ||
13 | + | ||
14 | + var finished = false | ||
15 | + var results = new Array(pending) | ||
16 | + | ||
17 | + fns.forEach(context ? function (fn, i) { | ||
18 | + fn.call(context, maybeDone(i)) | ||
19 | + } : function (fn, i) { | ||
20 | + fn(maybeDone(i)) | ||
21 | + }) | ||
22 | + | ||
23 | + function maybeDone(i) { | ||
24 | + return function (err, result) { | ||
25 | + if (finished) return; | ||
26 | + | ||
27 | + if (err) { | ||
28 | + callback(err, results) | ||
29 | + finished = true | ||
30 | + return | ||
31 | + } | ||
32 | + | ||
33 | + results[i] = result | ||
34 | + | ||
35 | + if (!--pending) callback(null, results); | ||
36 | + } | ||
37 | + } | ||
38 | +} | ||
39 | + | ||
40 | +function noop() {} |
1 | +{ | ||
2 | + "name": "array-parallel", | ||
3 | + "description": "Call an array of asynchronous functions in parallel", | ||
4 | + "version": "0.1.3", | ||
5 | + "scripts": { | ||
6 | + "test": "node test" | ||
7 | + }, | ||
8 | + "author": { | ||
9 | + "name": "Jonathan Ong", | ||
10 | + "email": "me@jongleberry.com", | ||
11 | + "url": "http://jongleberry.com" | ||
12 | + }, | ||
13 | + "repository": { | ||
14 | + "type": "git", | ||
15 | + "url": "https://github.com/component/array-parallel.git" | ||
16 | + }, | ||
17 | + "bugs": { | ||
18 | + "url": "https://github.com/component/array-parallel/issues", | ||
19 | + "email": "me@jongleberry.com" | ||
20 | + }, | ||
21 | + "license": "MIT", | ||
22 | + "homepage": "https://github.com/component/array-parallel", | ||
23 | + "_id": "array-parallel@0.1.3", | ||
24 | + "dist": { | ||
25 | + "shasum": "8f785308926ed5aa478c47e64d1b334b6c0c947d", | ||
26 | + "tarball": "http://registry.npmjs.org/array-parallel/-/array-parallel-0.1.3.tgz" | ||
27 | + }, | ||
28 | + "_from": "array-parallel@~0.1.0", | ||
29 | + "_npmVersion": "1.3.17", | ||
30 | + "_npmUser": { | ||
31 | + "name": "jongleberry", | ||
32 | + "email": "jonathanrichardong@gmail.com" | ||
33 | + }, | ||
34 | + "maintainers": [ | ||
35 | + { | ||
36 | + "name": "jongleberry", | ||
37 | + "email": "jonathanrichardong@gmail.com" | ||
38 | + } | ||
39 | + ], | ||
40 | + "directories": {}, | ||
41 | + "_shasum": "8f785308926ed5aa478c47e64d1b334b6c0c947d", | ||
42 | + "_resolved": "https://registry.npmjs.org/array-parallel/-/array-parallel-0.1.3.tgz" | ||
43 | +} |
1 | +var assert = require('assert') | ||
2 | +var parallel = require('./') | ||
3 | + | ||
4 | +var a, b, c | ||
5 | +parallel([ | ||
6 | + function (done) { | ||
7 | + setTimeout(function () { | ||
8 | + done(null, a = 0) | ||
9 | + }, 5) | ||
10 | + }, | ||
11 | + function (done) { | ||
12 | + setTimeout(function () { | ||
13 | + done(null, b = 1) | ||
14 | + }, 10) | ||
15 | + }, | ||
16 | + function (done) { | ||
17 | + setTimeout(function () { | ||
18 | + done(null, c = 2) | ||
19 | + }, 15) | ||
20 | + } | ||
21 | +], function (err, results) { | ||
22 | + assert.equal(a, 0) | ||
23 | + assert.equal(b, 1) | ||
24 | + assert.equal(c, 2) | ||
25 | + | ||
26 | + assert.deepEqual(results, [0, 1, 2]) | ||
27 | +}) | ||
28 | + | ||
29 | +var d, e | ||
30 | +parallel([ | ||
31 | + function (done) { | ||
32 | + setTimeout(function () { | ||
33 | + d = 1 | ||
34 | + done(new Error('message')) | ||
35 | + }, 5) | ||
36 | + }, | ||
37 | + function (done) { | ||
38 | + setTimeout(function () { | ||
39 | + e = 2 | ||
40 | + done() | ||
41 | + }, 10) | ||
42 | + } | ||
43 | +], function (err) { | ||
44 | + assert.equal(err.message, 'message') | ||
45 | + assert.equal(d, 1) | ||
46 | + assert.equal(e, undefined) | ||
47 | +}) | ||
48 | + | ||
49 | +var context = 'hello' | ||
50 | +parallel([function (done) { | ||
51 | + assert.equal(this, context) | ||
52 | +}], context) | ||
53 | + | ||
54 | +var f | ||
55 | +parallel([function (done) { | ||
56 | + f = true | ||
57 | + done() | ||
58 | +}]) | ||
59 | + | ||
60 | +process.nextTick(function () { | ||
61 | + assert.equal(f, true) | ||
62 | +}) | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | +# Compiled source # | ||
2 | +################### | ||
3 | +*.com | ||
4 | +*.class | ||
5 | +*.dll | ||
6 | +*.exe | ||
7 | +*.o | ||
8 | +*.so | ||
9 | + | ||
10 | +# Packages # | ||
11 | +############ | ||
12 | +# it's better to unpack these files and commit the raw source | ||
13 | +# git has its own built in compression methods | ||
14 | +*.7z | ||
15 | +*.dmg | ||
16 | +*.gz | ||
17 | +*.iso | ||
18 | +*.jar | ||
19 | +*.rar | ||
20 | +*.tar | ||
21 | +*.zip | ||
22 | + | ||
23 | +# Logs and databases # | ||
24 | +###################### | ||
25 | +*.log | ||
26 | +*.sql | ||
27 | +*.sqlite | ||
28 | + | ||
29 | +# OS generated files # | ||
30 | +###################### | ||
31 | +.DS_Store* | ||
32 | +ehthumbs.db | ||
33 | +Icon? | ||
34 | +Thumbs.db | ||
35 | + | ||
36 | +# Node.js # | ||
37 | +########### | ||
38 | +lib-cov | ||
39 | +*.seed | ||
40 | +*.log | ||
41 | +*.csv | ||
42 | +*.dat | ||
43 | +*.out | ||
44 | +*.pid | ||
45 | +*.gz | ||
46 | + | ||
47 | +pids | ||
48 | +logs | ||
49 | +results | ||
50 | + | ||
51 | +node_modules | ||
52 | +npm-debug.log | ||
53 | + | ||
54 | +# Components # | ||
55 | +############## | ||
56 | + | ||
57 | +/build | ||
58 | +/components | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | +# Array Series [](https://travis-ci.org/component/array-series) | ||
2 | + | ||
3 | +Call an array of asynchronous functions in series | ||
4 | + | ||
5 | +### API | ||
6 | + | ||
7 | +#### series(fns[, context[, callback]]) | ||
8 | + | ||
9 | +```js | ||
10 | +var series = require('array-series') | ||
11 | + | ||
12 | +series([ | ||
13 | + function (done) { | ||
14 | + done() | ||
15 | + } | ||
16 | +], this, function (err) { | ||
17 | + | ||
18 | +}) | ||
19 | +``` | ||
20 | + | ||
21 | +#### fns | ||
22 | + | ||
23 | +`fns` is an array of functions to call in series. | ||
24 | +The argument signature should be: | ||
25 | + | ||
26 | +```js | ||
27 | +function (done) { | ||
28 | + done(new Error()) | ||
29 | + // or | ||
30 | + done() | ||
31 | +} | ||
32 | +``` | ||
33 | + | ||
34 | +That is, each function should only take a `done` as an argument. | ||
35 | +Each callback should only take an optional `Error` as an argument. | ||
36 | + | ||
37 | +#### context | ||
38 | + | ||
39 | +Optional context to pass to each `fn`. | ||
40 | +Basically `fn.call(context, done)`. | ||
41 | + | ||
42 | +#### callback(err) | ||
43 | + | ||
44 | +```js | ||
45 | +function (err) { | ||
46 | + | ||
47 | +} | ||
48 | +``` | ||
49 | + | ||
50 | +Only argument is an `Error` argument. | ||
51 | +It will return the first error in the series of functions that returns an error, | ||
52 | +and no function after will be called. | ||
53 | + | ||
54 | +### License | ||
55 | + | ||
56 | +The MIT License (MIT) | ||
57 | + | ||
58 | +Copyright (c) 2013 Jonathan Ong me@jongleberry.com | ||
59 | + | ||
60 | +Permission is hereby granted, free of charge, to any person obtaining a copy | ||
61 | +of this software and associated documentation files (the "Software"), to deal | ||
62 | +in the Software without restriction, including without limitation the rights | ||
63 | +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
64 | +copies of the Software, and to permit persons to whom the Software is | ||
65 | +furnished to do so, subject to the following conditions: | ||
66 | + | ||
67 | +The above copyright notice and this permission notice shall be included in | ||
68 | +all copies or substantial portions of the Software. | ||
69 | + | ||
70 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
71 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
72 | +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
73 | +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
74 | +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
75 | +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
76 | +THE SOFTWARE. | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | +{ | ||
2 | + "name": "array-series", | ||
3 | + "description": "Call an array of asynchronous functions in series", | ||
4 | + "repo": "component/array-series", | ||
5 | + "version": "0.1.5", | ||
6 | + "main": "index.js", | ||
7 | + "scripts": [ | ||
8 | + "index.js" | ||
9 | + ], | ||
10 | + "license": "MIT" | ||
11 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | +module.exports = function series(fns, context, callback) { | ||
2 | + if (!callback) { | ||
3 | + if (typeof context === 'function') { | ||
4 | + callback = context | ||
5 | + context = null | ||
6 | + } else { | ||
7 | + callback = noop | ||
8 | + } | ||
9 | + } | ||
10 | + | ||
11 | + if (!(fns && fns.length)) return callback(); | ||
12 | + | ||
13 | + fns = fns.slice(0) | ||
14 | + | ||
15 | + var call = context | ||
16 | + ? function () { | ||
17 | + fns.length | ||
18 | + ? fns.shift().call(context, next) | ||
19 | + : callback() | ||
20 | + } | ||
21 | + : function () { | ||
22 | + fns.length | ||
23 | + ? fns.shift()(next) | ||
24 | + : callback() | ||
25 | + } | ||
26 | + | ||
27 | + call() | ||
28 | + | ||
29 | + function next(err) { | ||
30 | + err ? callback(err) : call() | ||
31 | + } | ||
32 | +} | ||
33 | + | ||
34 | +function noop() {} |
1 | +{ | ||
2 | + "name": "array-series", | ||
3 | + "description": "Call an array of asynchronous functions in series", | ||
4 | + "version": "0.1.5", | ||
5 | + "scripts": { | ||
6 | + "test": "node test" | ||
7 | + }, | ||
8 | + "author": { | ||
9 | + "name": "Jonathan Ong", | ||
10 | + "email": "me@jongleberry.com", | ||
11 | + "url": "http://jongleberry.com" | ||
12 | + }, | ||
13 | + "repository": { | ||
14 | + "type": "git", | ||
15 | + "url": "https://github.com/component/array-series.git" | ||
16 | + }, | ||
17 | + "bugs": { | ||
18 | + "url": "https://github.com/component/array-series/issues", | ||
19 | + "email": "me@jongleberry.com" | ||
20 | + }, | ||
21 | + "license": "MIT", | ||
22 | + "homepage": "https://github.com/component/array-series", | ||
23 | + "_id": "array-series@0.1.5", | ||
24 | + "dist": { | ||
25 | + "shasum": "df5d37bfc5c2ef0755e2aa4f92feae7d4b5a972f", | ||
26 | + "tarball": "http://registry.npmjs.org/array-series/-/array-series-0.1.5.tgz" | ||
27 | + }, | ||
28 | + "_from": "array-series@~0.1.0", | ||
29 | + "_npmVersion": "1.3.17", | ||
30 | + "_npmUser": { | ||
31 | + "name": "jongleberry", | ||
32 | + "email": "jonathanrichardong@gmail.com" | ||
33 | + }, | ||
34 | + "maintainers": [ | ||
35 | + { | ||
36 | + "name": "jongleberry", | ||
37 | + "email": "jonathanrichardong@gmail.com" | ||
38 | + } | ||
39 | + ], | ||
40 | + "directories": {}, | ||
41 | + "_shasum": "df5d37bfc5c2ef0755e2aa4f92feae7d4b5a972f", | ||
42 | + "_resolved": "https://registry.npmjs.org/array-series/-/array-series-0.1.5.tgz" | ||
43 | +} |
1 | +var assert = require('assert') | ||
2 | +var series = require('./') | ||
3 | + | ||
4 | +var a, b, c | ||
5 | + | ||
6 | +series([ | ||
7 | + function (done) { | ||
8 | + a = 1 | ||
9 | + process.nextTick(done) | ||
10 | + check('a') | ||
11 | + }, | ||
12 | + function (done) { | ||
13 | + b = 2 | ||
14 | + process.nextTick(done) | ||
15 | + check('b') | ||
16 | + }, | ||
17 | + function (done) { | ||
18 | + c = 3 | ||
19 | + process.nextTick(done) | ||
20 | + check('c') | ||
21 | + } | ||
22 | +], function (err) { | ||
23 | + assert.ifError(err) | ||
24 | + assert.equal(a, 1) | ||
25 | + assert.equal(b, 2) | ||
26 | + assert.equal(c, 3) | ||
27 | +}) | ||
28 | + | ||
29 | +function check(x) { | ||
30 | + switch (x) { | ||
31 | + case 'a': | ||
32 | + assert.equal(a, 1) | ||
33 | + assert.equal(b, undefined) | ||
34 | + assert.equal(c, undefined) | ||
35 | + break | ||
36 | + case 'b': | ||
37 | + assert.equal(a, 1) | ||
38 | + assert.equal(b, 2) | ||
39 | + assert.equal(c, undefined) | ||
40 | + break | ||
41 | + case 'c': | ||
42 | + assert.equal(a, 1) | ||
43 | + assert.equal(b, 2) | ||
44 | + assert.equal(c, 3) | ||
45 | + break | ||
46 | + } | ||
47 | +} | ||
48 | + | ||
49 | +var context = 'hello' | ||
50 | +series([function (done) { | ||
51 | + assert.equal(this, context) | ||
52 | + done() | ||
53 | +}], context) | ||
54 | + | ||
55 | +var finished | ||
56 | +series([], function (err) { | ||
57 | + finished = true | ||
58 | +}) | ||
59 | + | ||
60 | +process.nextTick(function () { | ||
61 | + if (!finished) | ||
62 | + throw new Error('Failed with no functions.'); | ||
63 | +}) | ||
64 | + | ||
65 | +var r, d, o | ||
66 | +series([ | ||
67 | + function (done) { | ||
68 | + r = 1 | ||
69 | + process.nextTick(done) | ||
70 | + }, | ||
71 | + function (done) { | ||
72 | + d = 0 | ||
73 | + process.nextTick(function () { | ||
74 | + done(new Error('message')) | ||
75 | + }) | ||
76 | + }, | ||
77 | + function (done) { | ||
78 | + o = 0 | ||
79 | + process.nextTick(done) | ||
80 | + } | ||
81 | +], function (err) { | ||
82 | + assert.equal(err.message, 'message') | ||
83 | + assert.equal(r, 1) | ||
84 | + assert.equal(d, 0) | ||
85 | + assert.equal(o, undefined) | ||
86 | +}) | ||
87 | + | ||
88 | +console.log('Array series tests pass!') | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | + | ||
2 | +0.7.0 / 2012-05-04 | ||
3 | +================== | ||
4 | + | ||
5 | + * Added .component to package.json | ||
6 | + * Added debug.component.js build | ||
7 | + | ||
8 | +0.6.0 / 2012-03-16 | ||
9 | +================== | ||
10 | + | ||
11 | + * Added support for "-" prefix in DEBUG [Vinay Pulim] | ||
12 | + * Added `.enabled` flag to the node version [TooTallNate] | ||
13 | + | ||
14 | +0.5.0 / 2012-02-02 | ||
15 | +================== | ||
16 | + | ||
17 | + * Added: humanize diffs. Closes #8 | ||
18 | + * Added `debug.disable()` to the CS variant | ||
19 | + * Removed padding. Closes #10 | ||
20 | + * Fixed: persist client-side variant again. Closes #9 | ||
21 | + | ||
22 | +0.4.0 / 2012-02-01 | ||
23 | +================== | ||
24 | + | ||
25 | + * Added browser variant support for older browsers [TooTallNate] | ||
26 | + * Added `debug.enable('project:*')` to browser variant [TooTallNate] | ||
27 | + * Added padding to diff (moved it to the right) | ||
28 | + | ||
29 | +0.3.0 / 2012-01-26 | ||
30 | +================== | ||
31 | + | ||
32 | + * Added millisecond diff when isatty, otherwise UTC string | ||
33 | + | ||
34 | +0.2.0 / 2012-01-22 | ||
35 | +================== | ||
36 | + | ||
37 | + * Added wildcard support | ||
38 | + | ||
39 | +0.1.0 / 2011-12-02 | ||
40 | +================== | ||
41 | + | ||
42 | + * Added: remove colors unless stderr isatty [TooTallNate] | ||
43 | + | ||
44 | +0.0.1 / 2010-01-03 | ||
45 | +================== | ||
46 | + | ||
47 | + * Initial release |
1 | + | ||
2 | +# debug | ||
3 | + | ||
4 | + tiny node.js debugging utility. | ||
5 | + | ||
6 | +## Installation | ||
7 | + | ||
8 | +``` | ||
9 | +$ npm install debug | ||
10 | +``` | ||
11 | + | ||
12 | +## Example | ||
13 | + | ||
14 | + This module is modelled after node core's debugging technique, allowing you to enable one or more topic-specific debugging functions, for example core does the following within many modules: | ||
15 | + | ||
16 | +```js | ||
17 | +var debug; | ||
18 | +if (process.env.NODE_DEBUG && /cluster/.test(process.env.NODE_DEBUG)) { | ||
19 | + debug = function(x) { | ||
20 | + var prefix = process.pid + ',' + | ||
21 | + (process.env.NODE_WORKER_ID ? 'Worker' : 'Master'); | ||
22 | + console.error(prefix, x); | ||
23 | + }; | ||
24 | +} else { | ||
25 | + debug = function() { }; | ||
26 | +} | ||
27 | +``` | ||
28 | + | ||
29 | + This concept is extremely simple but it works well. With `debug` you simply invoke the exported function to generate your debug function, passing it a name which will determine if a noop function is returned, or a decorated `console.error`, so all of the `console` format string goodies you're used to work fine. A unique color is selected per-function for visibility. | ||
30 | + | ||
31 | +Example _app.js_: | ||
32 | + | ||
33 | +```js | ||
34 | +var debug = require('debug')('http') | ||
35 | + , http = require('http') | ||
36 | + , name = 'My App'; | ||
37 | + | ||
38 | +// fake app | ||
39 | + | ||
40 | +debug('booting %s', name); | ||
41 | + | ||
42 | +http.createServer(function(req, res){ | ||
43 | + debug(req.method + ' ' + req.url); | ||
44 | + res.end('hello\n'); | ||
45 | +}).listen(3000, function(){ | ||
46 | + debug('listening'); | ||
47 | +}); | ||
48 | + | ||
49 | +// fake worker of some kind | ||
50 | + | ||
51 | +require('./worker'); | ||
52 | +``` | ||
53 | + | ||
54 | +Example _worker.js_: | ||
55 | + | ||
56 | +```js | ||
57 | +var debug = require('debug')('worker'); | ||
58 | + | ||
59 | +setInterval(function(){ | ||
60 | + debug('doing some work'); | ||
61 | +}, 1000); | ||
62 | +``` | ||
63 | + | ||
64 | + The __DEBUG__ environment variable is then used to enable these based on space or comma-delimited names. Here are some examples: | ||
65 | + | ||
66 | +  | ||
67 | + | ||
68 | +  | ||
69 | + | ||
70 | +## Millisecond diff | ||
71 | + | ||
72 | + When actively developing an application it can be useful to see when the time spent between one `debug()` call and the next. Suppose for example you invoke `debug()` before requesting a resource, and after as well, the "+NNNms" will show you how much time was spent between calls. | ||
73 | + | ||
74 | +  | ||
75 | + | ||
76 | + When stdout is not a TTY, `Date#toUTCString()` is used, making it more useful for logging the debug information as shown below: | ||
77 | + | ||
78 | +  | ||
79 | + | ||
80 | +## Conventions | ||
81 | + | ||
82 | + If you're using this in one or more of your libraries, you _should_ use the name of your library so that developers may toggle debugging as desired without guessing names. If you have more than one debuggers you _should_ prefix them with your library name and use ":" to separate features. For example "bodyParser" from Connect would then be "connect:bodyParser". | ||
83 | + | ||
84 | +## Wildcards | ||
85 | + | ||
86 | + The "*" character may be used as a wildcard. Suppose for example your library has debuggers named "connect:bodyParser", "connect:compress", "connect:session", instead of listing all three with `DEBUG=connect:bodyParser,connect.compress,connect:session`, you may simply do `DEBUG=connect:*`, or to run everything using this module simply use `DEBUG=*`. | ||
87 | + | ||
88 | + You can also exclude specific debuggers by prefixing them with a "-" character. For example, `DEBUG=* -connect:*` would include all debuggers except those starting with "connect:". | ||
89 | + | ||
90 | +## Browser support | ||
91 | + | ||
92 | + Debug works in the browser as well, currently persisted by `localStorage`. For example if you have `worker:a` and `worker:b` as shown below, and wish to debug both type `debug.enable('worker:*')` in the console and refresh the page, this will remain until you disable with `debug.disable()`. | ||
93 | + | ||
94 | +```js | ||
95 | +a = debug('worker:a'); | ||
96 | +b = debug('worker:b'); | ||
97 | + | ||
98 | +setInterval(function(){ | ||
99 | + a('doing some work'); | ||
100 | +}, 1000); | ||
101 | + | ||
102 | +setInterval(function(){ | ||
103 | + a('doing some work'); | ||
104 | +}, 1200); | ||
105 | +``` | ||
106 | + | ||
107 | +## License | ||
108 | + | ||
109 | +(The MIT License) | ||
110 | + | ||
111 | +Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca> | ||
112 | + | ||
113 | +Permission is hereby granted, free of charge, to any person obtaining | ||
114 | +a copy of this software and associated documentation files (the | ||
115 | +'Software'), to deal in the Software without restriction, including | ||
116 | +without limitation the rights to use, copy, modify, merge, publish, | ||
117 | +distribute, sublicense, and/or sell copies of the Software, and to | ||
118 | +permit persons to whom the Software is furnished to do so, subject to | ||
119 | +the following conditions: | ||
120 | + | ||
121 | +The above copyright notice and this permission notice shall be | ||
122 | +included in all copies or substantial portions of the Software. | ||
123 | + | ||
124 | +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, | ||
125 | +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
126 | +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
127 | +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||
128 | +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | ||
129 | +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | ||
130 | +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | +;(function(){ | ||
2 | + | ||
3 | +/** | ||
4 | + * Create a debugger with the given `name`. | ||
5 | + * | ||
6 | + * @param {String} name | ||
7 | + * @return {Type} | ||
8 | + * @api public | ||
9 | + */ | ||
10 | + | ||
11 | +function debug(name) { | ||
12 | + if (!debug.enabled(name)) return function(){}; | ||
13 | + | ||
14 | + return function(fmt){ | ||
15 | + var curr = new Date; | ||
16 | + var ms = curr - (debug[name] || curr); | ||
17 | + debug[name] = curr; | ||
18 | + | ||
19 | + fmt = name | ||
20 | + + ' ' | ||
21 | + + fmt | ||
22 | + + ' +' + debug.humanize(ms); | ||
23 | + | ||
24 | + // This hackery is required for IE8 | ||
25 | + // where `console.log` doesn't have 'apply' | ||
26 | + window.console | ||
27 | + && console.log | ||
28 | + && Function.prototype.apply.call(console.log, console, arguments); | ||
29 | + } | ||
30 | +} | ||
31 | + | ||
32 | +/** | ||
33 | + * The currently active debug mode names. | ||
34 | + */ | ||
35 | + | ||
36 | +debug.names = []; | ||
37 | +debug.skips = []; | ||
38 | + | ||
39 | +/** | ||
40 | + * Enables a debug mode by name. This can include modes | ||
41 | + * separated by a colon and wildcards. | ||
42 | + * | ||
43 | + * @param {String} name | ||
44 | + * @api public | ||
45 | + */ | ||
46 | + | ||
47 | +debug.enable = function(name) { | ||
48 | + localStorage.debug = name; | ||
49 | + | ||
50 | + var split = (name || '').split(/[\s,]+/) | ||
51 | + , len = split.length; | ||
52 | + | ||
53 | + for (var i = 0; i < len; i++) { | ||
54 | + name = split[i].replace('*', '.*?'); | ||
55 | + if (name[0] === '-') { | ||
56 | + debug.skips.push(new RegExp('^' + name.substr(1) + '$')); | ||
57 | + } | ||
58 | + else { | ||
59 | + debug.names.push(new RegExp('^' + name + '$')); | ||
60 | + } | ||
61 | + } | ||
62 | +}; | ||
63 | + | ||
64 | +/** | ||
65 | + * Disable debug output. | ||
66 | + * | ||
67 | + * @api public | ||
68 | + */ | ||
69 | + | ||
70 | +debug.disable = function(){ | ||
71 | + debug.enable(''); | ||
72 | +}; | ||
73 | + | ||
74 | +/** | ||
75 | + * Humanize the given `ms`. | ||
76 | + * | ||
77 | + * @param {Number} m | ||
78 | + * @return {String} | ||
79 | + * @api private | ||
80 | + */ | ||
81 | + | ||
82 | +debug.humanize = function(ms) { | ||
83 | + var sec = 1000 | ||
84 | + , min = 60 * 1000 | ||
85 | + , hour = 60 * min; | ||
86 | + | ||
87 | + if (ms >= hour) return (ms / hour).toFixed(1) + 'h'; | ||
88 | + if (ms >= min) return (ms / min).toFixed(1) + 'm'; | ||
89 | + if (ms >= sec) return (ms / sec | 0) + 's'; | ||
90 | + return ms + 'ms'; | ||
91 | +}; | ||
92 | + | ||
93 | +/** | ||
94 | + * Returns true if the given mode name is enabled, false otherwise. | ||
95 | + * | ||
96 | + * @param {String} name | ||
97 | + * @return {Boolean} | ||
98 | + * @api public | ||
99 | + */ | ||
100 | + | ||
101 | +debug.enabled = function(name) { | ||
102 | + for (var i = 0, len = debug.skips.length; i < len; i++) { | ||
103 | + if (debug.skips[i].test(name)) { | ||
104 | + return false; | ||
105 | + } | ||
106 | + } | ||
107 | + for (var i = 0, len = debug.names.length; i < len; i++) { | ||
108 | + if (debug.names[i].test(name)) { | ||
109 | + return true; | ||
110 | + } | ||
111 | + } | ||
112 | + return false; | ||
113 | +}; | ||
114 | + | ||
115 | +// persist | ||
116 | + | ||
117 | +if (window.localStorage) debug.enable(localStorage.debug); | ||
118 | + module.exports = debug; | ||
119 | + | ||
120 | +})(); | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | + | ||
2 | +/** | ||
3 | + * Create a debugger with the given `name`. | ||
4 | + * | ||
5 | + * @param {String} name | ||
6 | + * @return {Type} | ||
7 | + * @api public | ||
8 | + */ | ||
9 | + | ||
10 | +function debug(name) { | ||
11 | + if (!debug.enabled(name)) return function(){}; | ||
12 | + | ||
13 | + return function(fmt){ | ||
14 | + var curr = new Date; | ||
15 | + var ms = curr - (debug[name] || curr); | ||
16 | + debug[name] = curr; | ||
17 | + | ||
18 | + fmt = name | ||
19 | + + ' ' | ||
20 | + + fmt | ||
21 | + + ' +' + debug.humanize(ms); | ||
22 | + | ||
23 | + // This hackery is required for IE8 | ||
24 | + // where `console.log` doesn't have 'apply' | ||
25 | + window.console | ||
26 | + && console.log | ||
27 | + && Function.prototype.apply.call(console.log, console, arguments); | ||
28 | + } | ||
29 | +} | ||
30 | + | ||
31 | +/** | ||
32 | + * The currently active debug mode names. | ||
33 | + */ | ||
34 | + | ||
35 | +debug.names = []; | ||
36 | +debug.skips = []; | ||
37 | + | ||
38 | +/** | ||
39 | + * Enables a debug mode by name. This can include modes | ||
40 | + * separated by a colon and wildcards. | ||
41 | + * | ||
42 | + * @param {String} name | ||
43 | + * @api public | ||
44 | + */ | ||
45 | + | ||
46 | +debug.enable = function(name) { | ||
47 | + localStorage.debug = name; | ||
48 | + | ||
49 | + var split = (name || '').split(/[\s,]+/) | ||
50 | + , len = split.length; | ||
51 | + | ||
52 | + for (var i = 0; i < len; i++) { | ||
53 | + name = split[i].replace('*', '.*?'); | ||
54 | + if (name[0] === '-') { | ||
55 | + debug.skips.push(new RegExp('^' + name.substr(1) + '$')); | ||
56 | + } | ||
57 | + else { | ||
58 | + debug.names.push(new RegExp('^' + name + '$')); | ||
59 | + } | ||
60 | + } | ||
61 | +}; | ||
62 | + | ||
63 | +/** | ||
64 | + * Disable debug output. | ||
65 | + * | ||
66 | + * @api public | ||
67 | + */ | ||
68 | + | ||
69 | +debug.disable = function(){ | ||
70 | + debug.enable(''); | ||
71 | +}; | ||
72 | + | ||
73 | +/** | ||
74 | + * Humanize the given `ms`. | ||
75 | + * | ||
76 | + * @param {Number} m | ||
77 | + * @return {String} | ||
78 | + * @api private | ||
79 | + */ | ||
80 | + | ||
81 | +debug.humanize = function(ms) { | ||
82 | + var sec = 1000 | ||
83 | + , min = 60 * 1000 | ||
84 | + , hour = 60 * min; | ||
85 | + | ||
86 | + if (ms >= hour) return (ms / hour).toFixed(1) + 'h'; | ||
87 | + if (ms >= min) return (ms / min).toFixed(1) + 'm'; | ||
88 | + if (ms >= sec) return (ms / sec | 0) + 's'; | ||
89 | + return ms + 'ms'; | ||
90 | +}; | ||
91 | + | ||
92 | +/** | ||
93 | + * Returns true if the given mode name is enabled, false otherwise. | ||
94 | + * | ||
95 | + * @param {String} name | ||
96 | + * @return {Boolean} | ||
97 | + * @api public | ||
98 | + */ | ||
99 | + | ||
100 | +debug.enabled = function(name) { | ||
101 | + for (var i = 0, len = debug.skips.length; i < len; i++) { | ||
102 | + if (debug.skips[i].test(name)) { | ||
103 | + return false; | ||
104 | + } | ||
105 | + } | ||
106 | + for (var i = 0, len = debug.names.length; i < len; i++) { | ||
107 | + if (debug.names[i].test(name)) { | ||
108 | + return true; | ||
109 | + } | ||
110 | + } | ||
111 | + return false; | ||
112 | +}; | ||
113 | + | ||
114 | +// persist | ||
115 | + | ||
116 | +if (window.localStorage) debug.enable(localStorage.debug); | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | + | ||
2 | +var debug = require('../')('http') | ||
3 | + , http = require('http') | ||
4 | + , name = 'My App'; | ||
5 | + | ||
6 | +// fake app | ||
7 | + | ||
8 | +debug('booting %s', name); | ||
9 | + | ||
10 | +http.createServer(function(req, res){ | ||
11 | + debug(req.method + ' ' + req.url); | ||
12 | + res.end('hello\n'); | ||
13 | +}).listen(3000, function(){ | ||
14 | + debug('listening'); | ||
15 | +}); | ||
16 | + | ||
17 | +// fake worker of some kind | ||
18 | + | ||
19 | +require('./worker'); | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | +<html> | ||
2 | + <head> | ||
3 | + <title>debug()</title> | ||
4 | + <script src="../debug.js"></script> | ||
5 | + <script> | ||
6 | + // type debug.enable('*') in | ||
7 | + // the console and refresh :) | ||
8 | + | ||
9 | + a = debug('worker:a'); | ||
10 | + b = debug('worker:b'); | ||
11 | + | ||
12 | + setInterval(function(){ | ||
13 | + a('doing some work'); | ||
14 | + }, 1000); | ||
15 | + | ||
16 | + setInterval(function(){ | ||
17 | + a('doing some work'); | ||
18 | + }, 1200); | ||
19 | + </script> | ||
20 | + </head> | ||
21 | + <body> | ||
22 | + | ||
23 | + </body> | ||
24 | +</html> |
1 | + | ||
2 | +// DEBUG=* node example/worker | ||
3 | +// DEBUG=worker:* node example/worker | ||
4 | +// DEBUG=worker:a node example/worker | ||
5 | +// DEBUG=worker:b node example/worker | ||
6 | + | ||
7 | +var a = require('../')('worker:a') | ||
8 | + , b = require('../')('worker:b'); | ||
9 | + | ||
10 | +function work() { | ||
11 | + a('doing lots of uninteresting work'); | ||
12 | + setTimeout(work, Math.random() * 1000); | ||
13 | +} | ||
14 | + | ||
15 | +work(); | ||
16 | + | ||
17 | +function workb() { | ||
18 | + b('doing some work'); | ||
19 | + setTimeout(workb, Math.random() * 2000); | ||
20 | +} | ||
21 | + | ||
22 | +workb(); | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | +;(function(){ |
1 | +{ | ||
2 | + "name": "debug", | ||
3 | + "version": "0.7.0", | ||
4 | + "description": "small debugging utility", | ||
5 | + "keywords": [ | ||
6 | + "debug", | ||
7 | + "log", | ||
8 | + "debugger" | ||
9 | + ], | ||
10 | + "author": { | ||
11 | + "name": "TJ Holowaychuk", | ||
12 | + "email": "tj@vision-media.ca" | ||
13 | + }, | ||
14 | + "dependencies": {}, | ||
15 | + "devDependencies": { | ||
16 | + "mocha": "*" | ||
17 | + }, | ||
18 | + "main": "index", | ||
19 | + "browserify": "debug.component.js", | ||
20 | + "engines": { | ||
21 | + "node": "*" | ||
22 | + }, | ||
23 | + "component": { | ||
24 | + "scripts": { | ||
25 | + "debug": "debug.component.js" | ||
26 | + } | ||
27 | + }, | ||
28 | + "_id": "debug@0.7.0", | ||
29 | + "dist": { | ||
30 | + "shasum": "f5be05ec0434c992d79940e50b2695cfb2e01b08", | ||
31 | + "tarball": "http://registry.npmjs.org/debug/-/debug-0.7.0.tgz" | ||
32 | + }, | ||
33 | + "maintainers": [ | ||
34 | + { | ||
35 | + "name": "tjholowaychuk", | ||
36 | + "email": "tj@vision-media.ca" | ||
37 | + } | ||
38 | + ], | ||
39 | + "directories": {}, | ||
40 | + "_shasum": "f5be05ec0434c992d79940e50b2695cfb2e01b08", | ||
41 | + "_from": "debug@0.7.0", | ||
42 | + "_resolved": "https://registry.npmjs.org/debug/-/debug-0.7.0.tgz" | ||
43 | +} |
1 | +Apache License, Version 2.0 | ||
2 | + | ||
3 | +Copyright (c) 2011 Dominic Tarr | ||
4 | + | ||
5 | +Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | +you may not use this file except in compliance with the License. | ||
7 | +You may obtain a copy of the License at | ||
8 | + | ||
9 | + http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | + | ||
11 | +Unless required by applicable law or agreed to in writing, software | ||
12 | +distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | +See the License for the specific language governing permissions and | ||
15 | +limitations under the License. |
1 | +The MIT License | ||
2 | + | ||
3 | +Copyright (c) 2011 Dominic Tarr | ||
4 | + | ||
5 | +Permission is hereby granted, free of charge, | ||
6 | +to any person obtaining a copy of this software and | ||
7 | +associated documentation files (the "Software"), to | ||
8 | +deal in the Software without restriction, including | ||
9 | +without limitation the rights to use, copy, modify, | ||
10 | +merge, publish, distribute, sublicense, and/or sell | ||
11 | +copies of the Software, and to permit persons to whom | ||
12 | +the Software is furnished to do so, | ||
13 | +subject to the following conditions: | ||
14 | + | ||
15 | +The above copyright notice and this permission notice | ||
16 | +shall be included in all copies or substantial portions of the Software. | ||
17 | + | ||
18 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
19 | +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | ||
20 | +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
21 | +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR | ||
22 | +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | ||
23 | +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | ||
24 | +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
1 | +var Stream = require('stream') | ||
2 | + | ||
3 | +// through | ||
4 | +// | ||
5 | +// a stream that does nothing but re-emit the input. | ||
6 | +// useful for aggregating a series of changing but not ending streams into one stream) | ||
7 | + | ||
8 | +exports = module.exports = through | ||
9 | +through.through = through | ||
10 | + | ||
11 | +//create a readable writable stream. | ||
12 | + | ||
13 | +function through (write, end, opts) { | ||
14 | + write = write || function (data) { this.queue(data) } | ||
15 | + end = end || function () { this.queue(null) } | ||
16 | + | ||
17 | + var ended = false, destroyed = false, buffer = [], _ended = false | ||
18 | + var stream = new Stream() | ||
19 | + stream.readable = stream.writable = true | ||
20 | + stream.paused = false | ||
21 | + | ||
22 | +// stream.autoPause = !(opts && opts.autoPause === false) | ||
23 | + stream.autoDestroy = !(opts && opts.autoDestroy === false) | ||
24 | + | ||
25 | + stream.write = function (data) { | ||
26 | + write.call(this, data) | ||
27 | + return !stream.paused | ||
28 | + } | ||
29 | + | ||
30 | + function drain() { | ||
31 | + while(buffer.length && !stream.paused) { | ||
32 | + var data = buffer.shift() | ||
33 | + if(null === data) | ||
34 | + return stream.emit('end') | ||
35 | + else | ||
36 | + stream.emit('data', data) | ||
37 | + } | ||
38 | + } | ||
39 | + | ||
40 | + stream.queue = stream.push = function (data) { | ||
41 | +// console.error(ended) | ||
42 | + if(_ended) return stream | ||
43 | + if(data == null) _ended = true | ||
44 | + buffer.push(data) | ||
45 | + drain() | ||
46 | + return stream | ||
47 | + } | ||
48 | + | ||
49 | + //this will be registered as the first 'end' listener | ||
50 | + //must call destroy next tick, to make sure we're after any | ||
51 | + //stream piped from here. | ||
52 | + //this is only a problem if end is not emitted synchronously. | ||
53 | + //a nicer way to do this is to make sure this is the last listener for 'end' | ||
54 | + | ||
55 | + stream.on('end', function () { | ||
56 | + stream.readable = false | ||
57 | + if(!stream.writable && stream.autoDestroy) | ||
58 | + process.nextTick(function () { | ||
59 | + stream.destroy() | ||
60 | + }) | ||
61 | + }) | ||
62 | + | ||
63 | + function _end () { | ||
64 | + stream.writable = false | ||
65 | + end.call(stream) | ||
66 | + if(!stream.readable && stream.autoDestroy) | ||
67 | + stream.destroy() | ||
68 | + } | ||
69 | + | ||
70 | + stream.end = function (data) { | ||
71 | + if(ended) return | ||
72 | + ended = true | ||
73 | + if(arguments.length) stream.write(data) | ||
74 | + _end() // will emit or queue | ||
75 | + return stream | ||
76 | + } | ||
77 | + | ||
78 | + stream.destroy = function () { | ||
79 | + if(destroyed) return | ||
80 | + destroyed = true | ||
81 | + ended = true | ||
82 | + buffer.length = 0 | ||
83 | + stream.writable = stream.readable = false | ||
84 | + stream.emit('close') | ||
85 | + return stream | ||
86 | + } | ||
87 | + | ||
88 | + stream.pause = function () { | ||
89 | + if(stream.paused) return | ||
90 | + stream.paused = true | ||
91 | + return stream | ||
92 | + } | ||
93 | + | ||
94 | + stream.resume = function () { | ||
95 | + if(stream.paused) { | ||
96 | + stream.paused = false | ||
97 | + stream.emit('resume') | ||
98 | + } | ||
99 | + drain() | ||
100 | + //may have become paused again, | ||
101 | + //as drain emits 'data'. | ||
102 | + if(!stream.paused) | ||
103 | + stream.emit('drain') | ||
104 | + return stream | ||
105 | + } | ||
106 | + return stream | ||
107 | +} | ||
108 | + |
1 | +{ | ||
2 | + "name": "through", | ||
3 | + "version": "2.3.6", | ||
4 | + "description": "simplified stream construction", | ||
5 | + "main": "index.js", | ||
6 | + "scripts": { | ||
7 | + "test": "set -e; for t in test/*.js; do node $t; done" | ||
8 | + }, | ||
9 | + "devDependencies": { | ||
10 | + "stream-spec": "~0.3.5", | ||
11 | + "tape": "~2.3.2", | ||
12 | + "from": "~0.1.3" | ||
13 | + }, | ||
14 | + "keywords": [ | ||
15 | + "stream", | ||
16 | + "streams", | ||
17 | + "user-streams", | ||
18 | + "pipe" | ||
19 | + ], | ||
20 | + "author": { | ||
21 | + "name": "Dominic Tarr", | ||
22 | + "email": "dominic.tarr@gmail.com", | ||
23 | + "url": "dominictarr.com" | ||
24 | + }, | ||
25 | + "license": "MIT", | ||
26 | + "repository": { | ||
27 | + "type": "git", | ||
28 | + "url": "https://github.com/dominictarr/through.git" | ||
29 | + }, | ||
30 | + "homepage": "http://github.com/dominictarr/through", | ||
31 | + "testling": { | ||
32 | + "browsers": [ | ||
33 | + "ie/8..latest", | ||
34 | + "ff/15..latest", | ||
35 | + "chrome/20..latest", | ||
36 | + "safari/5.1..latest" | ||
37 | + ], | ||
38 | + "files": "test/*.js" | ||
39 | + }, | ||
40 | + "gitHead": "19ed9b7e84efe7c3e3c8be80f29390b1620e13c0", | ||
41 | + "bugs": { | ||
42 | + "url": "https://github.com/dominictarr/through/issues" | ||
43 | + }, | ||
44 | + "_id": "through@2.3.6", | ||
45 | + "_shasum": "26681c0f524671021d4e29df7c36bce2d0ecf2e8", | ||
46 | + "_from": "through@~2.3.1", | ||
47 | + "_npmVersion": "1.4.26", | ||
48 | + "_npmUser": { | ||
49 | + "name": "dominictarr", | ||
50 | + "email": "dominic.tarr@gmail.com" | ||
51 | + }, | ||
52 | + "maintainers": [ | ||
53 | + { | ||
54 | + "name": "dominictarr", | ||
55 | + "email": "dominic.tarr@gmail.com" | ||
56 | + } | ||
57 | + ], | ||
58 | + "dist": { | ||
59 | + "shasum": "26681c0f524671021d4e29df7c36bce2d0ecf2e8", | ||
60 | + "tarball": "http://registry.npmjs.org/through/-/through-2.3.6.tgz" | ||
61 | + }, | ||
62 | + "directories": {}, | ||
63 | + "_resolved": "https://registry.npmjs.org/through/-/through-2.3.6.tgz" | ||
64 | +} |
1 | +#through | ||
2 | + | ||
3 | +[](http://travis-ci.org/dominictarr/through) | ||
4 | +[](https://ci.testling.com/dominictarr/through) | ||
5 | + | ||
6 | +Easy way to create a `Stream` that is both `readable` and `writable`. | ||
7 | + | ||
8 | +* Pass in optional `write` and `end` methods. | ||
9 | +* `through` takes care of pause/resume logic if you use `this.queue(data)` instead of `this.emit('data', data)`. | ||
10 | +* Use `this.pause()` and `this.resume()` to manage flow. | ||
11 | +* Check `this.paused` to see current flow state. (`write` always returns `!this.paused`). | ||
12 | + | ||
13 | +This function is the basis for most of the synchronous streams in | ||
14 | +[event-stream](http://github.com/dominictarr/event-stream). | ||
15 | + | ||
16 | +``` js | ||
17 | +var through = require('through') | ||
18 | + | ||
19 | +through(function write(data) { | ||
20 | + this.queue(data) //data *must* not be null | ||
21 | + }, | ||
22 | + function end () { //optional | ||
23 | + this.queue(null) | ||
24 | + }) | ||
25 | +``` | ||
26 | + | ||
27 | +Or, can also be used _without_ buffering on pause, use `this.emit('data', data)`, | ||
28 | +and this.emit('end') | ||
29 | + | ||
30 | +``` js | ||
31 | +var through = require('through') | ||
32 | + | ||
33 | +through(function write(data) { | ||
34 | + this.emit('data', data) | ||
35 | + //this.pause() | ||
36 | + }, | ||
37 | + function end () { //optional | ||
38 | + this.emit('end') | ||
39 | + }) | ||
40 | +``` | ||
41 | + | ||
42 | +## Extended Options | ||
43 | + | ||
44 | +You will probably not need these 99% of the time. | ||
45 | + | ||
46 | +### autoDestroy=false | ||
47 | + | ||
48 | +By default, `through` emits close when the writable | ||
49 | +and readable side of the stream has ended. | ||
50 | +If that is not desired, set `autoDestroy=false`. | ||
51 | + | ||
52 | +``` js | ||
53 | +var through = require('through') | ||
54 | + | ||
55 | +//like this | ||
56 | +var ts = through(write, end, {autoDestroy: false}) | ||
57 | +//or like this | ||
58 | +var ts = through(write, end) | ||
59 | +ts.autoDestroy = false | ||
60 | +``` | ||
61 | + | ||
62 | +## License | ||
63 | + | ||
64 | +MIT / Apache2 |
1 | +var from = require('from') | ||
2 | +var through = require('../') | ||
3 | + | ||
4 | +var tape = require('tape') | ||
5 | + | ||
6 | +tape('simple async example', function (t) { | ||
7 | + | ||
8 | + var n = 0, expected = [1,2,3,4,5], actual = [] | ||
9 | + from(expected) | ||
10 | + .pipe(through(function(data) { | ||
11 | + this.pause() | ||
12 | + n ++ | ||
13 | + setTimeout(function(){ | ||
14 | + console.log('pushing data', data) | ||
15 | + this.push(data) | ||
16 | + this.resume() | ||
17 | + }.bind(this), 300) | ||
18 | + })).pipe(through(function(data) { | ||
19 | + console.log('pushing data second time', data); | ||
20 | + this.push(data) | ||
21 | + })).on('data', function (d) { | ||
22 | + actual.push(d) | ||
23 | + }).on('end', function() { | ||
24 | + t.deepEqual(actual, expected) | ||
25 | + t.end() | ||
26 | + }) | ||
27 | + | ||
28 | +}) |
1 | +var test = require('tape') | ||
2 | +var through = require('../') | ||
3 | + | ||
4 | +// must emit end before close. | ||
5 | + | ||
6 | +test('end before close', function (assert) { | ||
7 | + var ts = through() | ||
8 | + ts.autoDestroy = false | ||
9 | + var ended = false, closed = false | ||
10 | + | ||
11 | + ts.on('end', function () { | ||
12 | + assert.ok(!closed) | ||
13 | + ended = true | ||
14 | + }) | ||
15 | + ts.on('close', function () { | ||
16 | + assert.ok(ended) | ||
17 | + closed = true | ||
18 | + }) | ||
19 | + | ||
20 | + ts.write(1) | ||
21 | + ts.write(2) | ||
22 | + ts.write(3) | ||
23 | + ts.end() | ||
24 | + assert.ok(ended) | ||
25 | + assert.notOk(closed) | ||
26 | + ts.destroy() | ||
27 | + assert.ok(closed) | ||
28 | + assert.end() | ||
29 | +}) | ||
30 | + |
1 | +var test = require('tape') | ||
2 | +var through = require('../') | ||
3 | + | ||
4 | +// must emit end before close. | ||
5 | + | ||
6 | +test('buffering', function(assert) { | ||
7 | + var ts = through(function (data) { | ||
8 | + this.queue(data) | ||
9 | + }, function () { | ||
10 | + this.queue(null) | ||
11 | + }) | ||
12 | + | ||
13 | + var ended = false, actual = [] | ||
14 | + | ||
15 | + ts.on('data', actual.push.bind(actual)) | ||
16 | + ts.on('end', function () { | ||
17 | + ended = true | ||
18 | + }) | ||
19 | + | ||
20 | + ts.write(1) | ||
21 | + ts.write(2) | ||
22 | + ts.write(3) | ||
23 | + assert.deepEqual(actual, [1, 2, 3]) | ||
24 | + ts.pause() | ||
25 | + ts.write(4) | ||
26 | + ts.write(5) | ||
27 | + ts.write(6) | ||
28 | + assert.deepEqual(actual, [1, 2, 3]) | ||
29 | + ts.resume() | ||
30 | + assert.deepEqual(actual, [1, 2, 3, 4, 5, 6]) | ||
31 | + ts.pause() | ||
32 | + ts.end() | ||
33 | + assert.ok(!ended) | ||
34 | + ts.resume() | ||
35 | + assert.ok(ended) | ||
36 | + assert.end() | ||
37 | +}) | ||
38 | + | ||
39 | +test('buffering has data in queue, when ends', function (assert) { | ||
40 | + | ||
41 | + /* | ||
42 | + * If stream ends while paused with data in the queue, | ||
43 | + * stream should still emit end after all data is written | ||
44 | + * on resume. | ||
45 | + */ | ||
46 | + | ||
47 | + var ts = through(function (data) { | ||
48 | + this.queue(data) | ||
49 | + }, function () { | ||
50 | + this.queue(null) | ||
51 | + }) | ||
52 | + | ||
53 | + var ended = false, actual = [] | ||
54 | + | ||
55 | + ts.on('data', actual.push.bind(actual)) | ||
56 | + ts.on('end', function () { | ||
57 | + ended = true | ||
58 | + }) | ||
59 | + | ||
60 | + ts.pause() | ||
61 | + ts.write(1) | ||
62 | + ts.write(2) | ||
63 | + ts.write(3) | ||
64 | + ts.end() | ||
65 | + assert.deepEqual(actual, [], 'no data written yet, still paused') | ||
66 | + assert.ok(!ended, 'end not emitted yet, still paused') | ||
67 | + ts.resume() | ||
68 | + assert.deepEqual(actual, [1, 2, 3], 'resumed, all data should be delivered') | ||
69 | + assert.ok(ended, 'end should be emitted once all data was delivered') | ||
70 | + assert.end(); | ||
71 | +}) |
1 | +var test = require('tape') | ||
2 | +var through = require('../') | ||
3 | + | ||
4 | +// must emit end before close. | ||
5 | + | ||
6 | +test('end before close', function (assert) { | ||
7 | + var ts = through() | ||
8 | + var ended = false, closed = false | ||
9 | + | ||
10 | + ts.on('end', function () { | ||
11 | + assert.ok(!closed) | ||
12 | + ended = true | ||
13 | + }) | ||
14 | + ts.on('close', function () { | ||
15 | + assert.ok(ended) | ||
16 | + closed = true | ||
17 | + }) | ||
18 | + | ||
19 | + ts.write(1) | ||
20 | + ts.write(2) | ||
21 | + ts.write(3) | ||
22 | + ts.end() | ||
23 | + assert.ok(ended) | ||
24 | + assert.ok(closed) | ||
25 | + assert.end() | ||
26 | +}) | ||
27 | + | ||
28 | +test('end only once', function (t) { | ||
29 | + | ||
30 | + var ts = through() | ||
31 | + var ended = false, closed = false | ||
32 | + | ||
33 | + ts.on('end', function () { | ||
34 | + t.equal(ended, false) | ||
35 | + ended = true | ||
36 | + }) | ||
37 | + | ||
38 | + ts.queue(null) | ||
39 | + ts.queue(null) | ||
40 | + ts.queue(null) | ||
41 | + | ||
42 | + ts.resume() | ||
43 | + | ||
44 | + t.end() | ||
45 | +}) |
1 | + | ||
2 | +var test = require('tape') | ||
3 | +var spec = require('stream-spec') | ||
4 | +var through = require('../') | ||
5 | + | ||
6 | +/* | ||
7 | + I'm using these two functions, and not streams and pipe | ||
8 | + so there is less to break. if this test fails it must be | ||
9 | + the implementation of _through_ | ||
10 | +*/ | ||
11 | + | ||
12 | +function write(array, stream) { | ||
13 | + array = array.slice() | ||
14 | + function next() { | ||
15 | + while(array.length) | ||
16 | + if(stream.write(array.shift()) === false) | ||
17 | + return stream.once('drain', next) | ||
18 | + | ||
19 | + stream.end() | ||
20 | + } | ||
21 | + | ||
22 | + next() | ||
23 | +} | ||
24 | + | ||
25 | +function read(stream, callback) { | ||
26 | + var actual = [] | ||
27 | + stream.on('data', function (data) { | ||
28 | + actual.push(data) | ||
29 | + }) | ||
30 | + stream.once('end', function () { | ||
31 | + callback(null, actual) | ||
32 | + }) | ||
33 | + stream.once('error', function (err) { | ||
34 | + callback(err) | ||
35 | + }) | ||
36 | +} | ||
37 | + | ||
38 | +test('simple defaults', function(assert) { | ||
39 | + | ||
40 | + var l = 1000 | ||
41 | + , expected = [] | ||
42 | + | ||
43 | + while(l--) expected.push(l * Math.random()) | ||
44 | + | ||
45 | + var t = through() | ||
46 | + var s = spec(t).through().pausable() | ||
47 | + | ||
48 | + read(t, function (err, actual) { | ||
49 | + assert.ifError(err) | ||
50 | + assert.deepEqual(actual, expected) | ||
51 | + assert.end() | ||
52 | + }) | ||
53 | + | ||
54 | + t.on('close', s.validate) | ||
55 | + | ||
56 | + write(expected, t) | ||
57 | +}); | ||
58 | + | ||
59 | +test('simple functions', function(assert) { | ||
60 | + | ||
61 | + var l = 1000 | ||
62 | + , expected = [] | ||
63 | + | ||
64 | + while(l--) expected.push(l * Math.random()) | ||
65 | + | ||
66 | + var t = through(function (data) { | ||
67 | + this.emit('data', data*2) | ||
68 | + }) | ||
69 | + var s = spec(t).through().pausable() | ||
70 | + | ||
71 | + | ||
72 | + read(t, function (err, actual) { | ||
73 | + assert.ifError(err) | ||
74 | + assert.deepEqual(actual, expected.map(function (data) { | ||
75 | + return data*2 | ||
76 | + })) | ||
77 | + assert.end() | ||
78 | + }) | ||
79 | + | ||
80 | + t.on('close', s.validate) | ||
81 | + | ||
82 | + write(expected, t) | ||
83 | +}) | ||
84 | + | ||
85 | +test('pauses', function(assert) { | ||
86 | + | ||
87 | + var l = 1000 | ||
88 | + , expected = [] | ||
89 | + | ||
90 | + while(l--) expected.push(l) //Math.random()) | ||
91 | + | ||
92 | + var t = through() | ||
93 | + | ||
94 | + var s = spec(t) | ||
95 | + .through() | ||
96 | + .pausable() | ||
97 | + | ||
98 | + t.on('data', function () { | ||
99 | + if(Math.random() > 0.1) return | ||
100 | + t.pause() | ||
101 | + process.nextTick(function () { | ||
102 | + t.resume() | ||
103 | + }) | ||
104 | + }) | ||
105 | + | ||
106 | + read(t, function (err, actual) { | ||
107 | + assert.ifError(err) | ||
108 | + assert.deepEqual(actual, expected) | ||
109 | + }) | ||
110 | + | ||
111 | + t.on('close', function () { | ||
112 | + s.validate() | ||
113 | + assert.end() | ||
114 | + }) | ||
115 | + | ||
116 | + write(expected, t) | ||
117 | +}) |
1 | +{ | ||
2 | + "name": "gm", | ||
3 | + "description": "GraphicsMagick and ImageMagick for node.js", | ||
4 | + "version": "1.17.0", | ||
5 | + "author": { | ||
6 | + "name": "Aaron Heckmann", | ||
7 | + "email": "aaron.heckmann+github@gmail.com" | ||
8 | + }, | ||
9 | + "keywords": [ | ||
10 | + "graphics", | ||
11 | + "magick", | ||
12 | + "image", | ||
13 | + "graphicsmagick", | ||
14 | + "imagemagick", | ||
15 | + "gm", | ||
16 | + "convert", | ||
17 | + "identify", | ||
18 | + "compare" | ||
19 | + ], | ||
20 | + "engines": { | ||
21 | + "node": ">= 0.8.0" | ||
22 | + }, | ||
23 | + "bugs": { | ||
24 | + "url": "http://github.com/aheckmann/gm/issues" | ||
25 | + }, | ||
26 | + "licenses": [ | ||
27 | + { | ||
28 | + "type": "MIT", | ||
29 | + "url": "http://www.opensource.org/licenses/mit-license.php" | ||
30 | + } | ||
31 | + ], | ||
32 | + "main": "./index", | ||
33 | + "scripts": { | ||
34 | + "test": "make test-unit; make test;" | ||
35 | + }, | ||
36 | + "repository": { | ||
37 | + "type": "git", | ||
38 | + "url": "https://github.com/aheckmann/gm.git" | ||
39 | + }, | ||
40 | + "license": "MIT", | ||
41 | + "devDependencies": { | ||
42 | + "gleak": "0.4.0", | ||
43 | + "async": "~0.2.7" | ||
44 | + }, | ||
45 | + "dependencies": { | ||
46 | + "debug": "0.7.0", | ||
47 | + "array-series": "~0.1.0", | ||
48 | + "array-parallel": "~0.1.0", | ||
49 | + "through": "~2.3.1" | ||
50 | + }, | ||
51 | + "gitHead": "2fb65bac7c09ab8b09e87b791b146b82c209076e", | ||
52 | + "homepage": "https://github.com/aheckmann/gm", | ||
53 | + "_id": "gm@1.17.0", | ||
54 | + "_shasum": "27a261e0bdfee3d373d24b5a27bd249057355068", | ||
55 | + "_from": "gm@", | ||
56 | + "_npmVersion": "1.4.28", | ||
57 | + "_npmUser": { | ||
58 | + "name": "rwky", | ||
59 | + "email": "admin@rwky.net" | ||
60 | + }, | ||
61 | + "maintainers": [ | ||
62 | + { | ||
63 | + "name": "aaron", | ||
64 | + "email": "aaron.heckmann+github@gmail.com" | ||
65 | + }, | ||
66 | + { | ||
67 | + "name": "jongleberry", | ||
68 | + "email": "me@jongleberry.com" | ||
69 | + }, | ||
70 | + { | ||
71 | + "name": "rwky", | ||
72 | + "email": "admin@rwky.net" | ||
73 | + }, | ||
74 | + { | ||
75 | + "name": "fragphace", | ||
76 | + "email": "fragphace@gmail.com" | ||
77 | + } | ||
78 | + ], | ||
79 | + "dist": { | ||
80 | + "shasum": "27a261e0bdfee3d373d24b5a27bd249057355068", | ||
81 | + "tarball": "http://registry.npmjs.org/gm/-/gm-1.17.0.tgz" | ||
82 | + }, | ||
83 | + "directories": {}, | ||
84 | + "_resolved": "https://registry.npmjs.org/gm/-/gm-1.17.0.tgz" | ||
85 | +} |
samples/s3/input.json
0 → 100644
1 | +{ | ||
2 | + "Records":[ | ||
3 | + { | ||
4 | + "eventVersion":"2.0", | ||
5 | + "eventSource":"aws:s3", | ||
6 | + "awsRegion":"us-east-1", | ||
7 | + "eventTime":"1970-01-01T00:00:00.000Z", | ||
8 | + "eventName":"ObjectCreated:Put", | ||
9 | + "userIdentity":{ | ||
10 | + "principalId":"AIDAJDPLRKLG7UEXAMPLE" | ||
11 | + }, | ||
12 | + "requestParameters":{ | ||
13 | + "sourceIPAddress":"127.0.0.1" | ||
14 | + }, | ||
15 | + "responseElements":{ | ||
16 | + "x-amz-request-id":"C3D13FE58DE4C810", | ||
17 | + "x-amz-id-2":"FMyUVURIY8/IgAtTv8xRjskZQpcIZ9KG4V5Wp6S7S/JRWeUWerMUE5JgHvANOjpD" | ||
18 | + }, | ||
19 | + "s3":{ | ||
20 | + "s3SchemaVersion":"1.0", | ||
21 | + "configurationId":"testConfigRule", | ||
22 | + "bucket":{ | ||
23 | + "name":"sourcebucket", | ||
24 | + "ownerIdentity":{ | ||
25 | + "principalId":"A3NL1KOZZKExample" | ||
26 | + }, | ||
27 | + "arn":"arn:aws:s3:::sourcebucket" | ||
28 | + }, | ||
29 | + "object":{ | ||
30 | + "key":"HappyFace.jpg", | ||
31 | + "size":1024, | ||
32 | + "eTag":"d41d8cd98f00b204e9800998ecf8427e", | ||
33 | + "versionId":"096fKKXTRTtl3on89fVO.nfljtsv6qko" | ||
34 | + } | ||
35 | + } | ||
36 | + } | ||
37 | + ] | ||
38 | +} |
samples/s3/roles.cf
0 → 100644
1 | +{ | ||
2 | + "AWSTemplateFormatVersion": "2010-09-09", | ||
3 | + "Resources": { | ||
4 | + "ExecRole": { | ||
5 | + "Type": "AWS::IAM::Role", | ||
6 | + "Properties": { | ||
7 | + "AssumeRolePolicyDocument": { | ||
8 | + "Version" : "2012-10-17", | ||
9 | + "Statement": [ { | ||
10 | + "Effect": "Allow", | ||
11 | + "Principal": { | ||
12 | + "Service": [ "lambda.amazonaws.com" ] | ||
13 | + }, | ||
14 | + "Action": [ "sts:AssumeRole" ] | ||
15 | + } ] | ||
16 | + } | ||
17 | + } | ||
18 | + }, | ||
19 | + "ExecRolePolicies": { | ||
20 | + "Type": "AWS::IAM::Policy", | ||
21 | + "Properties": { | ||
22 | + "PolicyName": "ExecRolePolicy", | ||
23 | + "PolicyDocument": { | ||
24 | + "Version" : "2012-10-17", | ||
25 | + "Statement": [ { | ||
26 | + "Effect": "Allow", | ||
27 | + "Action": [ | ||
28 | + "logs:*" | ||
29 | + ], | ||
30 | + "Resource": "arn:aws:logs:*:*:*" | ||
31 | + }, | ||
32 | + { | ||
33 | + "Effect": "Allow", | ||
34 | + "Action": [ | ||
35 | + "s3:GetObject", | ||
36 | + "s3:PutObject" | ||
37 | + ], | ||
38 | + "Resource": [ | ||
39 | + "arn:aws:s3:::*" | ||
40 | + ] | ||
41 | + } ] | ||
42 | + }, | ||
43 | + "Roles": [ { "Ref": "ExecRole" } ] | ||
44 | + } | ||
45 | + }, | ||
46 | + "InvokeRole": { | ||
47 | + "Type": "AWS::IAM::Role", | ||
48 | + "Properties": { | ||
49 | + "AssumeRolePolicyDocument": { | ||
50 | + "Version" : "2012-10-17", | ||
51 | + "Statement": [ { | ||
52 | + "Effect": "Allow", | ||
53 | + "Principal": { | ||
54 | + "Service": [ "s3.amazonaws.com" ] | ||
55 | + }, | ||
56 | + "Action": [ "sts:AssumeRole" ], | ||
57 | + "Condition": { | ||
58 | + "ArnLike": { | ||
59 | + "sts:ExternalId": "arn:aws:s3:::*" | ||
60 | + } | ||
61 | + } | ||
62 | + } ] | ||
63 | + } | ||
64 | + } | ||
65 | + }, | ||
66 | + "InvokeRolePolicies": { | ||
67 | + "Type": "AWS::IAM::Policy", | ||
68 | + "Properties": { | ||
69 | + "PolicyName": "ExecRolePolicy", | ||
70 | + "PolicyDocument": { | ||
71 | + "Version" : "2012-10-17", | ||
72 | + "Statement": [ | ||
73 | + { | ||
74 | + "Effect": "Allow", | ||
75 | + "Resource": [ | ||
76 | + "*" | ||
77 | + ], | ||
78 | + "Action": [ | ||
79 | + "lambda:InvokeFunction" | ||
80 | + ] | ||
81 | + } | ||
82 | + ] | ||
83 | + }, | ||
84 | + "Roles": [ { "Ref": "InvokeRole" } ] | ||
85 | + } | ||
86 | + } | ||
87 | + } | ||
88 | +} |
-
Please register or login to post a comment