read input data from terminal with prompt

This commit is contained in:
ericdouglas 2015-06-01 11:43:19 -03:00
parent 4a8585e6b8
commit 7b02d7403f
348 changed files with 34157 additions and 36 deletions

View File

@ -1,53 +1,58 @@
//// Create a variable x and assign value 3 to it
//var x = 3;
//
//// Bind x to value 9
//x *= x; // or x = x * x;
//console.log( x );
// Modules
var prompt = require( 'prompt' );
//// read input data from terminal
//process.stdin.resume();
//
//console.log( 'Enter a number:' );
//process.stdin.setEncoding( 'utf8' );
//
//process.stdin.on( 'data', function( input ) {
//
// console.log( typeof( input ));
// console.log( input );
//
// process.exit();
//
//});
// Create a variable x and assign value 3 to it
var x = 3;
// Bind x to value 9
x *= x; // or x = x * x;
console.log( x );
// Verify if a integer number is even or odd.
// If odd, verify if the number is divisible by 3
// read input data from terminal
process.stdin.resume();
console.log( 'Enter a integer:' );
process.stdin.setEncoding( 'utf8' );
console.log( '' );
process.stdin.on( 'data', function( input ) {
prompt.start();
prompt.get({
name : 'number',
description : 'Enter a number'
}, function( err, results ) {
var int = parseInt( input, 10 );
if ( int % 2 === 0 ) {
console.log( 'Even' );
} else {
console.log( 'Odd' );
if ( int % 3 !== 0 ) {
console.log( 'And not divisible by 3' );
}
}
process.exit();
console.log( results.number );
});
// // Verify if a integer number is even or odd.
// // If odd, verify if the number is divisible by 3
// // read input data from terminal
// process.stdin.resume();
// console.log( 'Enter a integer:' );
// process.stdin.setEncoding( 'utf8' );
// process.stdin.on( 'data', function( input ) {
// var int = parseInt( input, 10 );
// if ( int % 2 === 0 ) {
// console.log( 'Even' );
// } else {
// console.log( 'Odd' );
// if ( int % 3 !== 0 ) {
// console.log( 'And not divisible by 3' );
// }
// }
// process.exit();
// });
// Find the lowest of three numbers

View File

@ -0,0 +1,54 @@
{
"passfail": false,
"maxerr": 100,
"browser": false,
"node": true,
"rhino": false,
"couch": true,
"wsh": true,
"jquery": true,
"prototypejs": false,
"mootools": false,
"dojo": false,
"devel": false,
"es5": true,
"strict": false,
"globalstrict": false,
"asi": false,
"lastsemic": true,
"laxbreak": true,
"laxcomma": false,
"bitwise": false,
"boss": false,
"curly": true,
"eqeqeq": true,
"eqnull": false,
"evil": false,
"expr": false,
"forin": false,
"immed": false,
"latedef": false,
"loopfunc": true,
"noarg": true,
"regexp": true,
"regexdash": false,
"scripturl": true,
"shadow": true,
"supernew": true,
"undef": true,
"newcap": true,
"noempty": true,
"nonew": true,
"nomen": false,
"onevar": true,
"plusplus": false,
"sub": true,
"trailing": true,
"white": false,
"indent": 2
}

View File

@ -0,0 +1,3 @@
node_modules/
node_modules/*
npm-debug.log

View File

@ -0,0 +1,10 @@
language: node_js
node_js:
- 0.8
- 0.10
notifications:
email:
- travis@nodejitsu.com
irc: "irc.freenode.org#nodejitsu"

View File

@ -0,0 +1,13 @@
0.2.7 / 2012-08-30
==================
* Fixed handling of numeric inputs with parseFloat
* Fixed overwriting of non-string inputs
* Added support for boolean types
0.2.6 / 2012-08-12
==================
* Added allowance of empty default values

View File

@ -0,0 +1,19 @@
Copyright (c) 2010 Nodejitsu Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,317 @@
# prompt [![Build Status](https://secure.travis-ci.org/flatiron/prompt.svg)](http://travis-ci.org/flatiron/prompt)
A beautiful command-line prompt for node.js
## Features
* prompts the user for input
* supports validation and defaults
* hides passwords
## Usage
Using prompt is relatively straight forward. There are two core methods you should be aware of: `prompt.get()` and `prompt.addProperties()`. There methods take strings representing property names in addition to objects for complex property validation (and more). There are a number of [examples][0] that you should examine for detailed usage.
### Getting Basic Prompt Information
Getting started with `prompt` is easy. Lets take a look at `examples/simple-prompt.js`:
``` js
var prompt = require('prompt');
//
// Start the prompt
//
prompt.start();
//
// Get two properties from the user: username and email
//
prompt.get(['username', 'email'], function (err, result) {
//
// Log the results.
//
console.log('Command-line input received:');
console.log(' username: ' + result.username);
console.log(' email: ' + result.email);
});
```
This will result in the following command-line output:
```
$ node examples/simple-prompt.js
prompt: username: some-user
prompt: email: some-user@some-place.org
Command-line input received:
username: some-user
email: some-user@some-place.org
```
### Prompting with Validation, Default Values, and More (Complex Properties)
In addition to prompting the user with simple string prompts, there is a robust API for getting and validating complex information from a command-line prompt. Here's a quick sample:
``` js
var schema = {
properties: {
name: {
pattern: /^[a-zA-Z\s\-]+$/,
message: 'Name must be only letters, spaces, or dashes',
required: true
},
password: {
hidden: true
}
}
};
//
// Start the prompt
//
prompt.start();
//
// Get two properties from the user: email, password
//
prompt.get(schema, function (err, result) {
//
// Log the results.
//
console.log('Command-line input received:');
console.log(' name: ' + result.name);
console.log(' password: ' + result.password);
});
```
Pretty easy right? The output from the above script is:
```
$ node examples/property-prompt.js
prompt: name: nodejitsu000
error: Invalid input for name
error: Name must be only letters, spaces, or dashes
prompt: name: Nodejitsu Inc
prompt: password:
Command-line input received:
name: Nodejitsu Inc
password: some-password
```
## Valid Property Settings
`prompt` understands JSON-schema with a few extra parameters and uses [revalidator](https://github.com/flatiron/revalidator) for validation.
Here's an overview of the properties that may be used for validation and prompting controls:
``` js
{
description: 'Enter your password', // Prompt displayed to the user. If not supplied name will be used.
type: 'string', // Specify the type of input to expect.
pattern: /^\w+$/, // Regular expression that input must be valid against.
message: 'Password must be letters', // Warning message to display if validation fails.
hidden: true, // If true, characters entered will not be output to console.
default: 'lamepassword', // Default value to use if no value is entered.
required: true // If true, value entered must be non-empty.
before: function(value) { return 'v' + value; } // Runs before node-prompt callbacks. It modifies user's input
}
```
Alternatives to `pattern` include `format` and `conform`, as documented in [revalidator](https://github.com/flatiron/revalidator).
Using `type: 'array'` has some special cases.
- `description` will not work in the schema if `type: 'array'` is defined.
- `maxItems` takes precedence over `minItems`.
- Arrays that do not have `maxItems` defined will require users to `SIGINT` (`^C`) before the array is ended.
- If `SIGINT` (`^C`) is triggered before `minItems` is met, a validation error will appear. This will require users to `SIGEOF` (`^D`) to end the input.
For more information on things such as `maxItems` and `minItems`, refer to the [revalidator](https://github.com/flatiron/revalidator) repository.
### Alternate Validation API:
Prompt, in addition to iterating over JSON-Schema properties, will also happily iterate over an array of validation objects given an extra 'name' property:
```js
var prompt = require('../lib/prompt');
//
// Start the prompt
//
prompt.start();
//
// Get two properties from the user: username and password
//
prompt.get([{
name: 'username',
required: true
}, {
name: 'password',
hidden: true,
conform: function (value) {
return true;
}
}], function (err, result) {
//
// Log the results.
//
console.log('Command-line input received:');
console.log(' username: ' + result.username);
console.log(' password: ' + result.password);
});
```
### Backward Compatibility
Note that, while this structure is similar to that used by prompt 0.1.x, that the object properties use the same names as in JSON-Schema. prompt 0.2.x is backward compatible with prompt 0.1.x except for asynchronous validation.
### Skipping Prompts
Sometimes power users may wish to skip promts and specify all data as command line options.
if a value is set as a property of `prompt.override` prompt will use that instead of
prompting the user.
``` js
//prompt-override.js
var prompt = require('prompt'),
optimist = require('optimist')
//
// set the overrides
//
prompt.override = optimist.argv
//
// Start the prompt
//
prompt.start();
//
// Get two properties from the user: username and email
//
prompt.get(['username', 'email'], function (err, result) {
//
// Log the results.
//
console.log('Command-line input received:');
console.log(' username: ' + result.username);
console.log(' email: ' + result.email);
})
//: node prompt-override.js --username USER --email EMAIL
```
### Adding Properties to an Object
A common use-case for prompting users for data from the command-line is to extend or create a configuration object that is passed onto the entry-point method for your CLI tool. `prompt` exposes a convenience method for doing just this:
``` js
var obj = {
password: 'lamepassword',
mindset: 'NY'
}
//
// Log the initial object.
//
console.log('Initial object to be extended:');
console.dir(obj);
//
// Add two properties to the empty object: username and email
//
prompt.addProperties(obj, ['username', 'email'], function (err) {
//
// Log the results.
//
console.log('Updated object received:');
console.dir(obj);
});
```
### Prompt history
You can use the `prompt.history()` method to get access to previous prompt input.
``` js
prompt.get([{
name: 'name',
description: 'Your name',
type: 'string',
required: true
}, {
name: 'surname',
description: 'Your surname',
type: 'string',
required: true,
message: 'Please dont use the demo credentials',
conform: function(surname) {
var name = prompt.history('name').value;
return (name !== 'John' || surname !== 'Smith');
}
}], function(err, results) {
console.log(results);
});
```
## Customizing your prompt
Aside from changing `property.message`, you can also change `prompt.message`
and `prompt.delimiter` to change the appearance of your prompt.
The basic structure of a prompt is this:
``` js
prompt.message + prompt.delimiter + property.message + prompt.delimiter;
```
The default `prompt.message` is "prompt," the default `prompt.delimiter` is
": ", and the default `property.message` is `property.name`.
Changing these allows you to customize the appearance of your prompts! In
addition, prompt supports ANSI color codes via the
[colors module](https://github.com/Marak/colors.js) for custom colors. For a
very colorful example:
``` js
var prompt = require("prompt");
//
// Setting these properties customizes the prompt.
//
prompt.message = "Question!".rainbow;
prompt.delimiter = "><".green;
prompt.start();
prompt.get({
properties: {
name: {
description: "What is your name?".magenta
}
}
}, function (err, result) {
console.log("You said your name is: ".cyan + result.name.cyan);
});
```
If you don't want colors, you can set
```js
var prompt = require('prompt');
prompt.colors = false;
```
## Installation
``` bash
$ [sudo] npm install prompt
```
## Running tests
``` bash
$ npm test
```
#### License: MIT
#### Author: [Charlie Robbins](http://github.com/indexzero)
#### Contributors: [Josh Holbrook](http://github.com/jesusabdullah), [Pavan Kumar Sunkara](http://github.com/pksunkara)
[0]: https://github.com/flatiron/prompt/tree/master/examples

View File

@ -0,0 +1,194 @@
/*--------------------- Layout and Typography ----------------------------*/
body {
font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif;
font-size: 15px;
line-height: 22px;
color: #252519;
margin: 0; padding: 0;
}
a {
color: #261a3b;
}
a:visited {
color: #261a3b;
}
p {
margin: 0 0 15px 0;
}
h4, h5, h6 {
color: #333;
margin: 6px 0 6px 0;
font-size: 13px;
}
h2, h3 {
margin-bottom: 0;
color: #000;
}
h1 {
margin-top: 40px;
margin-bottom: 15px;
color: #000;
}
#container {
position: relative;
}
#background {
position: fixed;
top: 0; left: 525px; right: 0; bottom: 0;
background: #f5f5ff;
border-left: 1px solid #e5e5ee;
z-index: -1;
}
#jump_to, #jump_page {
background: white;
-webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777;
-webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px;
font: 10px Arial;
text-transform: uppercase;
cursor: pointer;
text-align: right;
}
#jump_to, #jump_wrapper {
position: fixed;
right: 0; top: 0;
padding: 5px 10px;
}
#jump_wrapper {
padding: 0;
display: none;
}
#jump_to:hover #jump_wrapper {
display: block;
}
#jump_page {
padding: 5px 0 3px;
margin: 0 0 25px 25px;
}
#jump_page .source {
display: block;
padding: 5px 10px;
text-decoration: none;
border-top: 1px solid #eee;
}
#jump_page .source:hover {
background: #f5f5ff;
}
#jump_page .source:first-child {
}
table td {
border: 0;
outline: 0;
}
td.docs, th.docs {
max-width: 450px;
min-width: 450px;
min-height: 5px;
padding: 10px 25px 1px 50px;
overflow-x: hidden;
vertical-align: top;
text-align: left;
}
.docs pre {
margin: 15px 0 15px;
padding-left: 15px;
}
.docs p tt, .docs p code {
background: #f8f8ff;
border: 1px solid #dedede;
font-size: 12px;
padding: 0 0.2em;
}
.pilwrap {
position: relative;
}
.pilcrow {
font: 12px Arial;
text-decoration: none;
color: #454545;
position: absolute;
top: 3px; left: -20px;
padding: 1px 2px;
opacity: 0;
-webkit-transition: opacity 0.2s linear;
}
td.docs:hover .pilcrow {
opacity: 1;
}
td.code, th.code {
padding: 14px 15px 16px 25px;
width: 100%;
vertical-align: top;
background: #f5f5ff;
border-left: 1px solid #e5e5ee;
}
pre, tt, code {
font-size: 12px; line-height: 18px;
font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace;
margin: 0; padding: 0;
}
/*---------------------- Syntax Highlighting -----------------------------*/
td.linenos { background-color: #f0f0f0; padding-right: 10px; }
span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; }
body .hll { background-color: #ffffcc }
body .c { color: #408080; font-style: italic } /* Comment */
body .err { border: 1px solid #FF0000 } /* Error */
body .k { color: #954121 } /* Keyword */
body .o { color: #666666 } /* Operator */
body .cm { color: #408080; font-style: italic } /* Comment.Multiline */
body .cp { color: #BC7A00 } /* Comment.Preproc */
body .c1 { color: #408080; font-style: italic } /* Comment.Single */
body .cs { color: #408080; font-style: italic } /* Comment.Special */
body .gd { color: #A00000 } /* Generic.Deleted */
body .ge { font-style: italic } /* Generic.Emph */
body .gr { color: #FF0000 } /* Generic.Error */
body .gh { color: #000080; font-weight: bold } /* Generic.Heading */
body .gi { color: #00A000 } /* Generic.Inserted */
body .go { color: #808080 } /* Generic.Output */
body .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
body .gs { font-weight: bold } /* Generic.Strong */
body .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
body .gt { color: #0040D0 } /* Generic.Traceback */
body .kc { color: #954121 } /* Keyword.Constant */
body .kd { color: #954121; font-weight: bold } /* Keyword.Declaration */
body .kn { color: #954121; font-weight: bold } /* Keyword.Namespace */
body .kp { color: #954121 } /* Keyword.Pseudo */
body .kr { color: #954121; font-weight: bold } /* Keyword.Reserved */
body .kt { color: #B00040 } /* Keyword.Type */
body .m { color: #666666 } /* Literal.Number */
body .s { color: #219161 } /* Literal.String */
body .na { color: #7D9029 } /* Name.Attribute */
body .nb { color: #954121 } /* Name.Builtin */
body .nc { color: #0000FF; font-weight: bold } /* Name.Class */
body .no { color: #880000 } /* Name.Constant */
body .nd { color: #AA22FF } /* Name.Decorator */
body .ni { color: #999999; font-weight: bold } /* Name.Entity */
body .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
body .nf { color: #0000FF } /* Name.Function */
body .nl { color: #A0A000 } /* Name.Label */
body .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
body .nt { color: #954121; font-weight: bold } /* Name.Tag */
body .nv { color: #19469D } /* Name.Variable */
body .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
body .w { color: #bbbbbb } /* Text.Whitespace */
body .mf { color: #666666 } /* Literal.Number.Float */
body .mh { color: #666666 } /* Literal.Number.Hex */
body .mi { color: #666666 } /* Literal.Number.Integer */
body .mo { color: #666666 } /* Literal.Number.Oct */
body .sb { color: #219161 } /* Literal.String.Backtick */
body .sc { color: #219161 } /* Literal.String.Char */
body .sd { color: #219161; font-style: italic } /* Literal.String.Doc */
body .s2 { color: #219161 } /* Literal.String.Double */
body .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
body .sh { color: #219161 } /* Literal.String.Heredoc */
body .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
body .sx { color: #954121 } /* Literal.String.Other */
body .sr { color: #BB6688 } /* Literal.String.Regex */
body .s1 { color: #219161 } /* Literal.String.Single */
body .ss { color: #19469D } /* Literal.String.Symbol */
body .bp { color: #954121 } /* Name.Builtin.Pseudo */
body .vc { color: #19469D } /* Name.Variable.Class */
body .vg { color: #19469D } /* Name.Variable.Global */
body .vi { color: #19469D } /* Name.Variable.Instance */
body .il { color: #666666 } /* Literal.Number.Integer.Long */

View File

@ -0,0 +1,296 @@
<!DOCTYPE html> <html> <head> <title>prompt.js</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <link rel="stylesheet" media="all" href="docco.css" /> </head> <body> <div id="container"> <div id="background"></div> <table cellpadding="0" cellspacing="0"> <thead> <tr> <th class="docs"> <h1> prompt.js </h1> </th> <th class="code"> </th> </tr> </thead> <tbody> <tr id="section-1"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-1">&#182;</a> </div> </td> <td class="code"> <div class="highlight"><pre><span class="cm">/*</span>
<span class="cm"> * prompt.js: Simple prompt for prompting information from the command line </span>
<span class="cm"> *</span>
<span class="cm"> * (C) 2010, Nodejitsu Inc.</span>
<span class="cm"> *</span>
<span class="cm"> */</span>
<span class="kd">var</span> <span class="nx">events</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;events&#39;</span><span class="p">),</span>
<span class="nx">async</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;async&#39;</span><span class="p">),</span>
<span class="nx">colors</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;colors&#39;</span><span class="p">),</span>
<span class="nx">winston</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;winston&#39;</span><span class="p">),</span>
<span class="nx">stdio</span> <span class="o">=</span> <span class="nx">process</span><span class="p">.</span><span class="nx">binding</span><span class="p">(</span><span class="s1">&#39;stdio&#39;</span><span class="p">);</span></pre></div> </td> </tr> <tr id="section-2"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-2">&#182;</a> </div> <h3>@private function capitalize (str)</h3>
<h4>str {string} String to capitalize</h4>
<p>Capitalizes the string supplied.</p> </td> <td class="code"> <div class="highlight"><pre><span class="kd">function</span> <span class="nx">capitalize</span><span class="p">(</span><span class="nx">str</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">str</span><span class="p">.</span><span class="nx">charAt</span><span class="p">(</span><span class="mi">0</span><span class="p">).</span><span class="nx">toUpperCase</span><span class="p">()</span> <span class="o">+</span> <span class="nx">str</span><span class="p">.</span><span class="nx">slice</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="p">}</span>
<span class="kd">var</span> <span class="nx">prompt</span> <span class="o">=</span> <span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="nx">events</span><span class="p">.</span><span class="nx">EventEmitter</span><span class="p">.</span><span class="nx">prototype</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">logger</span> <span class="o">=</span> <span class="nx">prompt</span><span class="p">.</span><span class="nx">logger</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">winston</span><span class="p">.</span><span class="nx">Logger</span><span class="p">({</span>
<span class="nx">transports</span><span class="o">:</span> <span class="p">[</span>
<span class="k">new</span> <span class="p">(</span><span class="nx">winston</span><span class="p">.</span><span class="nx">transports</span><span class="p">.</span><span class="nx">Console</span><span class="p">)()</span>
<span class="p">]</span>
<span class="p">});</span>
<span class="nx">prompt</span><span class="p">.</span><span class="nx">started</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="nx">prompt</span><span class="p">.</span><span class="nx">paused</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="nx">prompt</span><span class="p">.</span><span class="nx">allowEmpty</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">stdin</span><span class="p">,</span> <span class="nx">stdout</span><span class="p">;</span></pre></div> </td> </tr> <tr id="section-3"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-3">&#182;</a> </div> <p>Create an empty object for the properties
known to <code>prompt</code></p> </td> <td class="code"> <div class="highlight"><pre><span class="nx">prompt</span><span class="p">.</span><span class="nx">properties</span> <span class="o">=</span> <span class="p">{};</span></pre></div> </td> </tr> <tr id="section-4"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-4">&#182;</a> </div> <p>Setup the default winston logger to use
the <code>cli</code> levels and colors.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nx">logger</span><span class="p">.</span><span class="nx">cli</span><span class="p">();</span></pre></div> </td> </tr> <tr id="section-5"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-5">&#182;</a> </div> <h3>function start (options)</h3>
<h4>@options {Object} <strong>Optional</strong> Options to consume by prompt</h4>
<p>Starts the prompt by listening to the appropriate events on <code>options.stdin</code>
and <code>options.stdout</code>. If no streams are supplied, then <code>process.stdin</code>
and <code>process.stdout</code> are used, respectively.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nx">prompt</span><span class="p">.</span><span class="nx">start</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">options</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">prompt</span><span class="p">.</span><span class="nx">started</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">options</span> <span class="o">=</span> <span class="nx">options</span> <span class="o">||</span> <span class="p">{};</span>
<span class="nx">stdin</span> <span class="o">=</span> <span class="nx">options</span><span class="p">.</span><span class="nx">stdin</span> <span class="o">||</span> <span class="nx">process</span><span class="p">.</span><span class="nx">openStdin</span><span class="p">();</span>
<span class="nx">stdout</span> <span class="o">=</span> <span class="nx">options</span><span class="p">.</span><span class="nx">stdout</span> <span class="o">||</span> <span class="nx">process</span><span class="p">.</span><span class="nx">stdout</span><span class="p">;</span>
<span class="nx">prompt</span><span class="p">.</span><span class="nx">allowEmpty</span> <span class="o">=</span> <span class="nx">options</span><span class="p">.</span><span class="nx">allowEmpty</span> <span class="o">||</span> <span class="kc">false</span><span class="p">;</span>
<span class="nx">process</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;SIGINT&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nx">stdout</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="s1">&#39;\n&#39;</span><span class="p">);</span>
<span class="nx">process</span><span class="p">.</span><span class="nx">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="p">})</span>
<span class="nx">prompt</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="s1">&#39;start&#39;</span><span class="p">);</span>
<span class="nx">prompt</span><span class="p">.</span><span class="nx">started</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="k">return</span> <span class="nx">prompt</span><span class="p">;</span>
<span class="p">};</span></pre></div> </td> </tr> <tr id="section-6"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-6">&#182;</a> </div> <h3>function pause ()</h3>
<p>Pauses input coming in from stdin</p> </td> <td class="code"> <div class="highlight"><pre><span class="nx">prompt</span><span class="p">.</span><span class="nx">pause</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">prompt</span><span class="p">.</span><span class="nx">started</span> <span class="o">||</span> <span class="nx">prompt</span><span class="p">.</span><span class="nx">paused</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">stdin</span><span class="p">.</span><span class="nx">pause</span><span class="p">();</span>
<span class="nx">prompt</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="s1">&#39;pause&#39;</span><span class="p">);</span>
<span class="nx">prompt</span><span class="p">.</span><span class="nx">paused</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="k">return</span> <span class="nx">prompt</span><span class="p">;</span>
<span class="p">};</span></pre></div> </td> </tr> <tr id="section-7"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-7">&#182;</a> </div> <h3>function resume ()</h3>
<p>Resumes input coming in from stdin </p> </td> <td class="code"> <div class="highlight"><pre><span class="nx">prompt</span><span class="p">.</span><span class="nx">resume</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">prompt</span><span class="p">.</span><span class="nx">started</span> <span class="o">||</span> <span class="o">!</span><span class="nx">prompt</span><span class="p">.</span><span class="nx">paused</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">stdin</span><span class="p">.</span><span class="nx">resume</span><span class="p">();</span>
<span class="nx">prompt</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="s1">&#39;resume&#39;</span><span class="p">);</span>
<span class="nx">prompt</span><span class="p">.</span><span class="nx">paused</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="k">return</span> <span class="nx">prompt</span><span class="p">;</span>
<span class="p">};</span></pre></div> </td> </tr> <tr id="section-8"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-8">&#182;</a> </div> <h3>function get (msg, [validator,] callback)</h3>
<h4>@msg {Array|Object|string} Set of variables to get input for.</h4>
<h4>@callback {function} Continuation to pass control to when complete.</h4>
<p>Gets input from the user via stdin for the specified message(s) <code>msg</code>.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nx">prompt</span><span class="p">.</span><span class="nx">get</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">msg</span><span class="p">,</span> <span class="nx">callback</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">vars</span> <span class="o">=</span> <span class="o">!</span><span class="nb">Array</span><span class="p">.</span><span class="nx">isArray</span><span class="p">(</span><span class="nx">msg</span><span class="p">)</span> <span class="o">?</span> <span class="p">[</span><span class="nx">msg</span><span class="p">]</span> <span class="o">:</span> <span class="nx">msg</span><span class="p">,</span>
<span class="nx">result</span> <span class="o">=</span> <span class="p">{};</span>
<span class="nx">vars</span> <span class="o">=</span> <span class="nx">vars</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">v</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">v</span> <span class="o">===</span> <span class="s1">&#39;string&#39;</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">v</span> <span class="o">=</span> <span class="nx">v</span><span class="p">.</span><span class="nx">toLowerCase</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nx">prompt</span><span class="p">.</span><span class="nx">properties</span><span class="p">[</span><span class="nx">v</span><span class="p">]</span> <span class="o">||</span> <span class="nx">v</span><span class="p">;</span>
<span class="p">});</span>
<span class="kd">function</span> <span class="nx">get</span><span class="p">(</span><span class="nx">target</span><span class="p">,</span> <span class="nx">next</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">prompt</span><span class="p">.</span><span class="nx">getInput</span><span class="p">(</span><span class="nx">target</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">line</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">next</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span>
<span class="p">}</span>
<span class="kd">var</span> <span class="nx">name</span> <span class="o">=</span> <span class="nx">target</span><span class="p">.</span><span class="nx">name</span> <span class="o">||</span> <span class="nx">target</span><span class="p">;</span>
<span class="nx">result</span><span class="p">[</span><span class="nx">name</span><span class="p">]</span> <span class="o">=</span> <span class="nx">line</span><span class="p">;</span>
<span class="nx">next</span><span class="p">();</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="nx">async</span><span class="p">.</span><span class="nx">forEachSeries</span><span class="p">(</span><span class="nx">vars</span><span class="p">,</span> <span class="nx">get</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">err</span> <span class="o">?</span> <span class="nx">callback</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="o">:</span> <span class="nx">callback</span><span class="p">(</span><span class="kc">null</span><span class="p">,</span> <span class="nx">result</span><span class="p">);</span>
<span class="p">});</span>
<span class="k">return</span> <span class="nx">prompt</span><span class="p">;</span>
<span class="p">};</span></pre></div> </td> </tr> <tr id="section-9"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-9">&#182;</a> </div> <h3>function getInput (msg, validator, callback)</h3>
<h4>@msg {Object|string} Variable to get input for.</h4>
<h4>@callback {function} Continuation to pass control to when complete.</h4>
<p>Gets input from the user via stdin for the specified message <code>msg</code>.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nx">prompt</span><span class="p">.</span><span class="nx">getInput</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">prop</span><span class="p">,</span> <span class="nx">callback</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">name</span> <span class="o">=</span> <span class="nx">prop</span><span class="p">.</span><span class="nx">message</span> <span class="o">||</span> <span class="nx">prop</span><span class="p">.</span><span class="nx">name</span> <span class="o">||</span> <span class="nx">prop</span><span class="p">,</span>
<span class="nx">raw</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;prompt&#39;</span><span class="p">,</span> <span class="s1">&#39;: &#39;</span> <span class="o">+</span> <span class="nx">name</span><span class="p">.</span><span class="nx">grey</span><span class="p">,</span> <span class="s1">&#39;: &#39;</span><span class="p">.</span><span class="nx">grey</span><span class="p">],</span>
<span class="nx">read</span> <span class="o">=</span> <span class="nx">prop</span><span class="p">.</span><span class="nx">hidden</span> <span class="o">?</span> <span class="nx">prompt</span><span class="p">.</span><span class="nx">readLineHidden</span> <span class="o">:</span> <span class="nx">prompt</span><span class="p">.</span><span class="nx">readLine</span><span class="p">,</span>
<span class="nx">length</span><span class="p">,</span> <span class="nx">msg</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">prop</span><span class="p">.</span><span class="k">default</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">raw</span><span class="p">.</span><span class="nx">splice</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="s1">&#39; (&#39;</span> <span class="o">+</span> <span class="nx">prop</span><span class="p">.</span><span class="k">default</span> <span class="o">+</span> <span class="s1">&#39;)&#39;</span><span class="p">);</span>
<span class="p">}</span>
</pre></div> </td> </tr> <tr id="section-10"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-10">&#182;</a> </div> <p>Calculate the raw length and colorize the prompt</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nx">length</span> <span class="o">=</span> <span class="nx">raw</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="s1">&#39;&#39;</span><span class="p">).</span><span class="nx">length</span><span class="p">;</span>
<span class="nx">raw</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="nx">raw</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
<span class="nx">msg</span> <span class="o">=</span> <span class="nx">raw</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="s1">&#39;&#39;</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">prop</span><span class="p">.</span><span class="nx">help</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">prop</span><span class="p">.</span><span class="nx">help</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">line</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">logger</span><span class="p">.</span><span class="nx">help</span><span class="p">(</span><span class="nx">line</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="nx">stdout</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="nx">msg</span><span class="p">);</span>
<span class="nx">prompt</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="s1">&#39;prompt&#39;</span><span class="p">,</span> <span class="nx">prop</span><span class="p">);</span>
<span class="nx">read</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="kc">null</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">line</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">callback</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">line</span> <span class="o">||</span> <span class="nx">line</span> <span class="o">===</span> <span class="s1">&#39;&#39;</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">line</span> <span class="o">=</span> <span class="nx">prop</span><span class="p">.</span><span class="k">default</span> <span class="o">||</span> <span class="nx">line</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">prop</span><span class="p">.</span><span class="nx">validator</span> <span class="o">||</span> <span class="nx">prop</span><span class="p">.</span><span class="nx">empty</span> <span class="o">===</span> <span class="kc">false</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">valid</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">prop</span><span class="p">.</span><span class="nx">validator</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">valid</span> <span class="o">=</span> <span class="nx">prop</span><span class="p">.</span><span class="nx">validator</span><span class="p">.</span><span class="nx">test</span>
<span class="o">?</span> <span class="nx">prop</span><span class="p">.</span><span class="nx">validator</span><span class="p">.</span><span class="nx">test</span><span class="p">(</span><span class="nx">line</span><span class="p">)</span>
<span class="o">:</span> <span class="nx">prop</span><span class="p">.</span><span class="nx">validator</span><span class="p">(</span><span class="nx">line</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">prop</span><span class="p">.</span><span class="nx">empty</span> <span class="o">===</span> <span class="kc">false</span> <span class="o">&amp;&amp;</span> <span class="nx">valid</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">valid</span> <span class="o">=</span> <span class="nx">line</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">;</span>
<span class="nx">prop</span><span class="p">.</span><span class="nx">warning</span> <span class="o">=</span> <span class="nx">prop</span><span class="p">.</span><span class="nx">warning</span> <span class="o">||</span> <span class="s1">&#39;You must supply a value.&#39;</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">valid</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">logger</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">&#39;Invalid input for &#39;</span> <span class="o">+</span> <span class="nx">name</span><span class="p">.</span><span class="nx">grey</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">prop</span><span class="p">.</span><span class="nx">warning</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">logger</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">prop</span><span class="p">.</span><span class="nx">warning</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">prompt</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="s1">&#39;invalid&#39;</span><span class="p">,</span> <span class="nx">prop</span><span class="p">,</span> <span class="nx">line</span><span class="p">);</span>
<span class="k">return</span> <span class="nx">prompt</span><span class="p">.</span><span class="nx">getInput</span><span class="p">(</span><span class="nx">prop</span><span class="p">,</span> <span class="nx">callback</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nx">logger</span><span class="p">.</span><span class="nx">input</span><span class="p">(</span><span class="nx">line</span><span class="p">.</span><span class="nx">yellow</span><span class="p">);</span>
<span class="nx">callback</span><span class="p">(</span><span class="kc">null</span><span class="p">,</span> <span class="nx">line</span><span class="p">);</span>
<span class="p">});</span>
<span class="k">return</span> <span class="nx">prompt</span><span class="p">;</span>
<span class="p">};</span></pre></div> </td> </tr> <tr id="section-11"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-11">&#182;</a> </div> <h3>function addProperties (obj, properties, callback)</h3>
<h4>@obj {Object} Object to add properties to</h4>
<h4>@properties {Array} List of properties to get values for</h4>
<h4>@callback {function} Continuation to pass control to when complete.</h4>
<p>Prompts the user for values each of the <code>properties</code> if <code>obj</code> does not already
have a value for the property. Responds with the modified object. </p> </td> <td class="code"> <div class="highlight"><pre><span class="nx">prompt</span><span class="p">.</span><span class="nx">addProperties</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">obj</span><span class="p">,</span> <span class="nx">properties</span><span class="p">,</span> <span class="nx">callback</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">properties</span> <span class="o">=</span> <span class="nx">properties</span><span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">prop</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">typeof</span> <span class="nx">obj</span><span class="p">[</span><span class="nx">prop</span><span class="p">]</span> <span class="o">===</span> <span class="s1">&#39;undefined&#39;</span><span class="p">;</span>
<span class="p">});</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">properties</span><span class="p">.</span><span class="nx">length</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">callback</span><span class="p">(</span><span class="nx">obj</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">prompt</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="nx">properties</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">results</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">callback</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">results</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">callback</span><span class="p">(</span><span class="kc">null</span><span class="p">,</span> <span class="nx">obj</span><span class="p">);</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">putNested</span> <span class="p">(</span><span class="nx">obj</span><span class="p">,</span> <span class="nx">path</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">last</span> <span class="o">=</span> <span class="nx">obj</span><span class="p">,</span> <span class="nx">key</span><span class="p">;</span>
<span class="k">while</span> <span class="p">(</span><span class="nx">path</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">key</span> <span class="o">=</span> <span class="nx">path</span><span class="p">.</span><span class="nx">shift</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">last</span><span class="p">[</span><span class="nx">key</span><span class="p">])</span> <span class="p">{</span>
<span class="nx">last</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span> <span class="o">=</span> <span class="p">{};</span>
<span class="p">}</span>
<span class="nx">last</span> <span class="o">=</span> <span class="nx">last</span><span class="p">[</span><span class="nx">key</span><span class="p">];</span>
<span class="p">}</span>
<span class="nx">last</span><span class="p">[</span><span class="nx">path</span><span class="p">.</span><span class="nx">shift</span><span class="p">()]</span> <span class="o">=</span> <span class="nx">value</span><span class="p">;</span>
<span class="p">}</span>
<span class="nb">Object</span><span class="p">.</span><span class="nx">keys</span><span class="p">(</span><span class="nx">results</span><span class="p">).</span><span class="nx">forEach</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">key</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">putNested</span><span class="p">(</span><span class="nx">obj</span><span class="p">,</span> <span class="nx">key</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="s1">&#39;.&#39;</span><span class="p">),</span> <span class="nx">results</span><span class="p">[</span><span class="nx">key</span><span class="p">]);</span>
<span class="p">});</span>
<span class="nx">callback</span><span class="p">(</span><span class="kc">null</span><span class="p">,</span> <span class="nx">obj</span><span class="p">);</span>
<span class="p">});</span>
<span class="k">return</span> <span class="nx">prompt</span><span class="p">;</span>
<span class="p">};</span></pre></div> </td> </tr> <tr id="section-12"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-12">&#182;</a> </div> <h3>function readLine (callback)</h3>
<h4>@callback {function} Continuation to respond to when complete</h4>
<p>Gets a single line of input from the user. </p> </td> <td class="code"> <div class="highlight"><pre><span class="nx">prompt</span><span class="p">.</span><span class="nx">readLine</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">callback</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">value</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">,</span> <span class="nx">buffer</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">;</span>
<span class="nx">prompt</span><span class="p">.</span><span class="nx">resume</span><span class="p">();</span>
<span class="nx">stdin</span><span class="p">.</span><span class="nx">setEncoding</span><span class="p">(</span><span class="s1">&#39;utf8&#39;</span><span class="p">);</span>
<span class="nx">stdin</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;error&#39;</span><span class="p">,</span> <span class="nx">callback</span><span class="p">);</span>
<span class="nx">stdin</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;data&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="nx">data</span> <span class="p">(</span><span class="nx">chunk</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">value</span> <span class="o">+=</span> <span class="nx">buffer</span> <span class="o">+</span> <span class="nx">chunk</span><span class="p">;</span>
<span class="nx">buffer</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">;</span>
<span class="nx">value</span> <span class="o">=</span> <span class="nx">value</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/\r/g</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">value</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="s1">&#39;\n&#39;</span><span class="p">)</span> <span class="o">!==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">value</span> <span class="o">!==</span> <span class="s1">&#39;\n&#39;</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">value</span> <span class="o">=</span> <span class="nx">value</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/^\n+/</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">buffer</span> <span class="o">=</span> <span class="nx">value</span><span class="p">.</span><span class="nx">substr</span><span class="p">(</span><span class="nx">value</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="s1">&#39;\n&#39;</span><span class="p">));</span>
<span class="nx">val</span> <span class="o">=</span> <span class="nx">value</span><span class="p">.</span><span class="nx">substr</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nx">value</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="s1">&#39;\n&#39;</span><span class="p">));</span>
<span class="nx">prompt</span><span class="p">.</span><span class="nx">pause</span><span class="p">();</span>
<span class="nx">stdin</span><span class="p">.</span><span class="nx">removeListener</span><span class="p">(</span><span class="s1">&#39;data&#39;</span><span class="p">,</span> <span class="nx">data</span><span class="p">);</span>
<span class="nx">stdin</span><span class="p">.</span><span class="nx">removeListener</span><span class="p">(</span><span class="s1">&#39;error&#39;</span><span class="p">,</span> <span class="nx">callback</span><span class="p">);</span>
<span class="nx">value</span> <span class="o">=</span> <span class="nx">value</span><span class="p">.</span><span class="nx">trim</span><span class="p">();</span>
<span class="nx">callback</span><span class="p">(</span><span class="kc">null</span><span class="p">,</span> <span class="nx">value</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="k">return</span> <span class="nx">prompt</span><span class="p">;</span>
<span class="p">};</span></pre></div> </td> </tr> <tr id="section-13"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-13">&#182;</a> </div> <h3>function readLineHidden (callback)</h3>
<h4>@callback {function} Continuation to respond to when complete</h4>
<p>Gets a single line of hidden input (i.e. <code>rawMode = true</code>) from the user. </p> </td> <td class="code"> <div class="highlight"><pre><span class="nx">prompt</span><span class="p">.</span><span class="nx">readLineHidden</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">callback</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">value</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">,</span> <span class="nx">buffer</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">;</span>
<span class="nx">stdio</span><span class="p">.</span><span class="nx">setRawMode</span><span class="p">(</span><span class="kc">true</span><span class="p">);</span>
<span class="nx">prompt</span><span class="p">.</span><span class="nx">resume</span><span class="p">();</span>
<span class="nx">stdin</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;error&#39;</span><span class="p">,</span> <span class="nx">callback</span><span class="p">);</span>
<span class="nx">stdin</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;data&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="nx">data</span> <span class="p">(</span><span class="nx">c</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">c</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span> <span class="o">+</span> <span class="nx">c</span><span class="p">;</span>
<span class="k">switch</span> <span class="p">(</span><span class="nx">c</span><span class="p">)</span> <span class="p">{</span>
<span class="k">case</span> <span class="s1">&#39;\n&#39;</span><span class="o">:</span> <span class="k">case</span> <span class="s1">&#39;\r&#39;</span><span class="o">:</span> <span class="k">case</span> <span class="s1">&#39;\r\n&#39;</span><span class="o">:</span> <span class="k">case</span> <span class="s1">&#39;\u0004&#39;</span><span class="o">:</span>
<span class="nx">stdio</span><span class="p">.</span><span class="nx">setRawMode</span><span class="p">(</span><span class="kc">false</span><span class="p">);</span>
<span class="nx">stdin</span><span class="p">.</span><span class="nx">removeListener</span><span class="p">(</span><span class="s1">&#39;data&#39;</span><span class="p">,</span> <span class="nx">data</span><span class="p">);</span>
<span class="nx">stdin</span><span class="p">.</span><span class="nx">removeListener</span><span class="p">(</span><span class="s1">&#39;error&#39;</span><span class="p">,</span> <span class="nx">callback</span><span class="p">);</span>
<span class="nx">value</span> <span class="o">=</span> <span class="nx">value</span><span class="p">.</span><span class="nx">trim</span><span class="p">();</span>
<span class="nx">stdout</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="s1">&#39;\n&#39;</span><span class="p">);</span>
<span class="nx">stdout</span><span class="p">.</span><span class="nx">flush</span><span class="p">();</span>
<span class="nx">prompt</span><span class="p">.</span><span class="nx">pause</span><span class="p">();</span>
<span class="k">return</span> <span class="nx">callback</span><span class="p">(</span><span class="kc">null</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span>
<span class="k">case</span> <span class="s1">&#39;\u0003&#39;</span><span class="o">:</span> <span class="k">case</span> <span class="s1">&#39;\0&#39;</span><span class="o">:</span>
<span class="nx">stdout</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="s1">&#39;\n&#39;</span><span class="p">);</span>
<span class="nx">process</span><span class="p">.</span><span class="nx">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="k">break</span><span class="p">;</span>
<span class="k">default</span><span class="o">:</span>
<span class="nx">value</span> <span class="o">+=</span> <span class="nx">buffer</span> <span class="o">+</span> <span class="nx">c</span>
<span class="nx">buffer</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">;</span>
<span class="k">break</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="k">return</span> <span class="nx">prompt</span><span class="p">;</span>
<span class="p">};</span>
</pre></div> </td> </tr> </tbody> </table> </div> </body> </html>

View File

@ -0,0 +1,35 @@
/*
* add-properties.js: Example of how to add properties to an object using prompt.
*
* (C) 2010, Nodejitsu Inc.
*
*/
var prompt = require('../lib/prompt');
//
// Start the prompt
//
prompt.start();
var obj = {
password: 'lamepassword',
mindset: 'NY'
}
//
// Log the initial object.
//
console.log('Initial object to be extended:');
console.dir(obj);
//
// Add two properties to the empty object: username and email
//
prompt.addProperties(obj, ['username', 'email'], function (err) {
//
// Log the results.
//
console.log('Updated object received:');
console.dir(obj);
});

View File

@ -0,0 +1,35 @@
/*
* existing-properties.js: Example of using prompt with predefined properties.
*
* (C) 2010, Nodejitsu Inc.
*
*/
var prompt = require('../lib/prompt');
prompt.properties = {
email: {
format: 'email',
message: 'Must be a valid email address'
},
password: {
hidden: true
}
};
//
// Start the prompt
//
prompt.start();
//
// Get two properties from the user: email, password
//
prompt.get(['email', 'password'], function (err, result) {
//
// Log the results.
//
console.log('Command-line input received:');
console.log(' email: ' + result.email);
console.log(' password: ' + result.password);
});

View File

@ -0,0 +1,44 @@
/*
* history.js: Example of using the prompt history capabilities.
*
* (C) 2010, Nodejitsu Inc.
*
*/
var prompt = require('../lib/prompt');
//
// Start the prompt
//
prompt.start();
var properties = {
properties: {
animal: {
description: 'Enter an animal',
default: 'dog',
pattern: /dog|cat/
},
sound: {
description: 'What sound does this animal make?',
conform: function (value) {
var animal = prompt.history(0).value;
return animal === 'dog' && value === 'woof'
|| animal === 'cat' && value === 'meow';
}
}
}
}
//
// Get two properties from the user
//
prompt.get(properties, function (err, result) {
//
// Log the results.
//
console.log('Command-line input received:');
console.log(' animal: ' + result.animal);
console.log(' sound: ' + result.sound);
});

View File

@ -0,0 +1,37 @@
/*
* property-prompt.js: Example of using prompt with complex properties.
*
* (C) 2010, Nodejitsu Inc.
*
*/
var prompt = require('../lib/prompt');
var schema = {
properties: {
url: {
required: true,
format: 'url'
},
auth: {
properties: {
username: {
required: true
},
password: {
required: true,
hidden: true
}
}
}
}
};
prompt.start();
prompt.get(schema, function (err, result) {
console.log('Command-line input received:');
console.log(' url: ' + result.url);
console.log(' auth:username: ' + result.auth.username);
console.log(' auth:password: ' + result.auth.password);
});

View File

@ -0,0 +1,36 @@
/*
* simple-prompt.js: Simple example of using prompt.
*
* (C) 2010, Nodejitsu Inc.
*
*/
var prompt = require('../lib/prompt');
//
// Start the prompt
//
prompt.start();
//
// Get two properties from the user: username and email
//
prompt.get([
{
name: 'username',
validator: /^[a-z]+$/,
warning: 'Username should consist only lowercase alphabets',
empty: false
},
{
name: 'email',
message: 'Email Address'
}
], function (err, result) {
//
// Log the results.
//
console.log('Command-line input received:');
console.log(' username: ' + result.username);
console.log(' email: ' + result.email);
});

View File

@ -0,0 +1,52 @@
/*
* override-validation.js: Example of using prompt with complex properties and command-line input.
*
* (C) 2010, Nodejitsu Inc.
*
*/
var prompt = require('../lib/prompt'),
optimist = require('optimist');
var schema = {
properties: {
name: {
pattern: /^[a-zA-Z\s-]+$/,
message: 'Name must be only letters, spaces, or dashes',
required: true
},
email: {
name: 'email',
format: 'email',
message: 'Must be a valid email address'
}
}
};
//
// Set the overrides
//
prompt.override = optimist.argv
//
// Start the prompt
//
prompt.start();
//
// Get two properties from the user: email, password
//
prompt.get(schema, function (err, result) {
//
// Log the results.
//
console.log('Command-line input received:');
console.log(' name: ' + result.name);
console.log(' email: ' + result.email);
});
// try running
// $ node ./override-validation.js --name USER --email EMAIL
// You will only be asked for email becasue it's invalid
// $ node ./override-validation.js --name h$acker --email me@example.com
// You will only be asked for email becasue it's invalid

View File

@ -0,0 +1,34 @@
/*
* password.js: Simple example of using prompt.
*
* (C) 2010, Nodejitsu Inc.
*
*/
var prompt = require('../lib/prompt');
//
// Start the prompt
//
prompt.start();
//
// Get two properties from the user: username and password
//
prompt.get([{
name: 'username',
required: true
}, {
name: 'password',
hidden: true,
conform: function (value) {
return true;
}
}], function (err, result) {
//
// Log the results.
//
console.log('Command-line input received:');
console.log(' username: ' + result.username);
console.log(' password: ' + result.password);
});

View File

@ -0,0 +1,36 @@
var prompt = require('../lib/prompt'),
optimist;
try {
optimist = require('optimist');
} catch (err) {
throw new Error([
'You need to install optimist before this example will work!',
'Try: `npm install optimist`.'
].join('\n'));
}
//
// Set the overrides
//
prompt.override = optimist.argv
//
// Start the prompt
//
prompt.start();
//
// Get two properties from the user: username and email
//
prompt.get(['username', 'email'], function (err, result) {
//
// Log the results.
//
console.log('Command-line input received:');
console.log(' username: ' + result.username);
console.log(' email: ' + result.email);
prompt.pause();
})
// $ node ./prompt-override.js --username USER --email EMAIL

View File

@ -0,0 +1,45 @@
/*
* property-prompt.js: Example of using prompt with complex properties.
*
* (C) 2010, Nodejitsu Inc.
*
*/
var prompt = require('../lib/prompt');
var schema = {
properties: {
name: {
pattern: /^[a-zA-Z\s-]+$/,
message: 'Name must be only letters, spaces, or dashes',
required: true
},
email: {
name: 'email',
format: 'email',
message: 'Must be a valid email address'
},
password: {
required: true,
hidden: true
}
}
};
//
// Start the prompt
//
prompt.start();
//
// Get two properties from the user: email, password
//
prompt.get(schema, function (err, result) {
//
// Log the results.
//
console.log('Command-line input received:');
console.log(' name: ' + result.name);
console.log(' email: ' + result.email);
console.log(' password: ' + result.password);
});

View File

@ -0,0 +1,25 @@
/*
* simple-prompt.js: Simple example of using prompt.
*
* (C) 2010, Nodejitsu Inc.
*
*/
var prompt = require('../lib/prompt');
//
// Start the prompt
//
prompt.start();
//
// Get two properties from the user: username and email
//
prompt.get(['username', 'email'], function (err, result) {
//
// Log the results.
//
console.log('Command-line input received:');
console.log(' username: ' + result.username);
console.log(' email: ' + result.email);
});

View File

@ -0,0 +1,32 @@
/*
* yes-or-no-prompt.js: Simple example of using prompt.
*
* (C) 2012, Nodejitsu Inc.
*
*/
var prompt = require('../lib/prompt');
//
// Start the prompt
//
prompt.start();
var property = {
name: 'yesno',
message: 'are you sure?',
validator: /y[es]*|n[o]?/,
warning: 'Must respond yes or no',
default: 'no'
};
//
// Get the simple yes or no property
//
prompt.get(property, function (err, result) {
//
// Log the results.
//
console.log('Command-line input received:');
console.log(' result: ' + result.yesno);
});

View File

@ -0,0 +1,756 @@
/*
* prompt.js: Simple prompt for prompting information from the command line
*
* (C) 2010, Nodejitsu Inc.
*
*/
var events = require('events'),
readline = require('readline'),
utile = require('utile'),
async = utile.async,
read = require('read'),
validate = require('revalidator').validate,
winston = require('winston');
//
// Monkey-punch readline.Interface to work-around
// https://github.com/joyent/node/issues/3860
//
readline.Interface.prototype.setPrompt = function(prompt, length) {
this._prompt = prompt;
if (length) {
this._promptLength = length;
} else {
var lines = prompt.split(/[\r\n]/);
var lastLine = lines[lines.length - 1];
this._promptLength = lastLine.replace(/\u001b\[(\d+(;\d+)*)?m/g, '').length;
}
};
//
// Expose version using `pkginfo`
//
require('pkginfo')(module, 'version');
var stdin, stdout, history = [];
var prompt = module.exports = Object.create(events.EventEmitter.prototype);
var logger = prompt.logger = new winston.Logger({
transports: [new (winston.transports.Console)()]
});
prompt.started = false;
prompt.paused = false;
prompt.allowEmpty = false;
prompt.message = 'prompt';
prompt.delimiter = ': ';
prompt.colors = true;
//
// Create an empty object for the properties
// known to `prompt`
//
prompt.properties = {};
//
// Setup the default winston logger to use
// the `cli` levels and colors.
//
logger.cli();
//
// ### function start (options)
// #### @options {Object} **Optional** Options to consume by prompt
// Starts the prompt by listening to the appropriate events on `options.stdin`
// and `options.stdout`. If no streams are supplied, then `process.stdin`
// and `process.stdout` are used, respectively.
//
prompt.start = function (options) {
if (prompt.started) {
return;
}
options = options || {};
stdin = options.stdin || process.stdin;
stdout = options.stdout || process.stdout;
//
// By default: Remember the last `10` prompt property /
// answer pairs and don't allow empty responses globally.
//
prompt.memory = options.memory || 10;
prompt.allowEmpty = options.allowEmpty || false;
prompt.message = options.message || prompt.message;
prompt.delimiter = options.delimiter || prompt.delimiter;
prompt.colors = options.colors || prompt.colors;
if (process.platform !== 'win32') {
// windows falls apart trying to deal with SIGINT
process.on('SIGINT', function () {
stdout.write('\n');
process.exit(1);
});
}
prompt.emit('start');
prompt.started = true;
return prompt;
};
//
// ### function pause ()
// Pauses input coming in from stdin
//
prompt.pause = function () {
if (!prompt.started || prompt.paused) {
return;
}
stdin.pause();
prompt.emit('pause');
prompt.paused = true;
return prompt;
};
//
// ### function resume ()
// Resumes input coming in from stdin
//
prompt.resume = function () {
if (!prompt.started || !prompt.paused) {
return;
}
stdin.resume();
prompt.emit('resume');
prompt.paused = false;
return prompt;
};
//
// ### function history (search)
// #### @search {Number|string} Index or property name to find.
// Returns the `property:value` pair from within the prompts
// `history` array.
//
prompt.history = function (search) {
if (typeof search === 'number') {
return history[search] || {};
}
var names = history.map(function (pair) {
return typeof pair.property === 'string'
? pair.property
: pair.property.name;
});
if (!~names.indexOf(search)) {
return null;
}
return history.filter(function (pair) {
return typeof pair.property === 'string'
? pair.property === search
: pair.property.name === search;
})[0];
};
//
// ### function get (schema, callback)
// #### @schema {Array|Object|string} Set of variables to get input for.
// #### @callback {function} Continuation to pass control to when complete.
// Gets input from the user via stdin for the specified message(s) `msg`.
//
prompt.get = function (schema, callback) {
//
// Transforms a full JSON-schema into an array describing path and sub-schemas.
// Used for iteration purposes.
//
function untangle(schema, path) {
var results = [];
path = path || [];
if (schema.properties) {
//
// Iterate over the properties in the schema and use recursion
// to process sub-properties.
//
Object.keys(schema.properties).forEach(function (key) {
var obj = {};
obj[key] = schema.properties[key];
//
// Concat a sub-untangling to the results.
//
results = results.concat(untangle(obj[key], path.concat(key)));
});
// Return the results.
return results;
}
//
// This is a schema "leaf".
//
return {
path: path,
schema: schema
};
}
//
// Iterate over the values in the schema, represented as
// a legit single-property object subschemas. Accepts `schema`
// of the forms:
//
// 'prop-name'
//
// ['string-name', { path: ['or-well-formed-subschema'], properties: ... }]
//
// { path: ['or-well-formed-subschema'], properties: ... ] }
//
// { properties: { 'schema-with-no-path' } }
//
// And transforms them all into
//
// { path: ['path', 'to', 'property'], properties: { path: { to: ...} } }
//
function iterate(schema, get, done) {
var iterator = [],
result = {};
if (typeof schema === 'string') {
//
// We can iterate over a single string.
//
iterator.push({
path: [schema],
schema: prompt.properties[schema.toLowerCase()] || {}
});
}
else if (Array.isArray(schema)) {
//
// An array of strings and/or single-prop schema and/or no-prop schema.
//
iterator = schema.map(function (element) {
if (typeof element === 'string') {
return {
path: [element],
schema: prompt.properties[element.toLowerCase()] || {}
};
}
else if (element.properties) {
return {
path: [Object.keys(element.properties)[0]],
schema: element.properties[Object.keys(element.properties)[0]]
};
}
else if (element.path && element.schema) {
return element;
}
else {
return {
path: [element.name || 'question'],
schema: element
};
}
});
}
else if (schema.properties) {
//
// Or a complete schema `untangle` it for use.
//
iterator = untangle(schema);
}
else {
//
// Or a partial schema and path.
// TODO: Evaluate need for this option.
//
iterator = [{
schema: schema.schema ? schema.schema : schema,
path: schema.path || [schema.name || 'question']
}];
}
//
// Now, iterate and assemble the result.
//
async.forEachSeries(iterator, function (branch, next) {
get(branch, function assembler(err, line) {
if (err) {
return next(err);
}
function build(path, line) {
var obj = {};
if (path.length) {
obj[path[0]] = build(path.slice(1), line);
return obj;
}
return line;
}
function attach(obj, attr) {
var keys;
if (typeof attr !== 'object' || attr instanceof Array) {
return attr;
}
keys = Object.keys(attr);
if (keys.length) {
if (!obj[keys[0]]) {
obj[keys[0]] = {};
}
obj[keys[0]] = attach(obj[keys[0]], attr[keys[0]]);
}
return obj;
}
result = attach(result, build(branch.path, line));
next();
});
}, function (err) {
return err ? done(err) : done(null, result);
});
}
iterate(schema, function get(target, next) {
prompt.getInput(target, function (err, line) {
return err ? next(err) : next(null, line);
});
}, callback);
return prompt;
};
//
// ### function confirm (msg, callback)
// #### @msg {Array|Object|string} set of message to confirm
// #### @callback {function} Continuation to pass control to when complete.
// Confirms a single or series of messages by prompting the user for a Y/N response.
// Returns `true` if ALL messages are answered in the affirmative, otherwise `false`
//
// `msg` can be a string, or object (or array of strings/objects).
// An object may have the following properties:
//
// {
// description: 'yes/no' // message to prompt user
// pattern: /^[yntf]{1}/i // optional - regex defining acceptable responses
// yes: /^[yt]{1}/i // optional - regex defining `affirmative` responses
// message: 'yes/no' // optional - message to display for invalid responses
// }
//
prompt.confirm = function (/* msg, options, callback */) {
var args = Array.prototype.slice.call(arguments),
msg = args.shift(),
callback = args.pop(),
opts = args.shift(),
vars = !Array.isArray(msg) ? [msg] : msg,
RX_Y = /^[yt]{1}/i,
RX_YN = /^[yntf]{1}/i;
function confirm(target, next) {
var yes = target.yes || RX_Y,
options = utile.mixin({
description: typeof target === 'string' ? target : target.description||'yes/no',
pattern: target.pattern || RX_YN,
name: 'confirm',
message: target.message || 'yes/no'
}, opts || {});
prompt.get([options], function (err, result) {
next(err ? false : yes.test(result[options.name]));
});
}
async.rejectSeries(vars, confirm, function(result) {
callback(null, result.length===0);
});
};
// Variables needed outside of getInput for multiline arrays.
var tmp = [];
// ### function getInput (prop, callback)
// #### @prop {Object|string} Variable to get input for.
// #### @callback {function} Continuation to pass control to when complete.
// Gets input from the user via stdin for the specified message `msg`.
//
prompt.getInput = function (prop, callback) {
var schema = prop.schema || prop,
propName = prop.path && prop.path.join(':') || prop,
storedSchema = prompt.properties[propName.toLowerCase()],
delim = prompt.delimiter,
defaultLine,
against,
hidden,
length,
valid,
name,
raw,
msg;
//
// If there is a stored schema for `propName` in `propmpt.properties`
// then use it.
//
if (schema instanceof Object && !Object.keys(schema).length &&
typeof storedSchema !== 'undefined') {
schema = storedSchema;
}
//
// Build a proper validation schema if we just have a string
// and no `storedSchema`.
//
if (typeof prop === 'string' && !storedSchema) {
schema = {};
}
schema = convert(schema);
defaultLine = schema.default;
name = prop.description || schema.description || propName;
raw = prompt.colors
? [prompt.message, delim + name.grey, delim.grey]
: [prompt.message, delim + name, delim];
prop = {
schema: schema,
path: propName.split(':')
};
//
// If the schema has no `properties` value then set
// it to an object containing the current schema
// for `propName`.
//
if (!schema.properties) {
schema = (function () {
var obj = { properties: {} };
obj.properties[propName] = schema;
return obj;
})();
}
//
// Handle overrides here.
// TODO: Make overrides nestable
//
if (prompt.override && prompt.override[propName]) {
if (prompt._performValidation(name, prop, prompt.override, schema, -1, callback)) {
return callback(null, prompt.override[propName]);
}
delete prompt.override[propName];
}
var type = (schema.properties && schema.properties[propName] &&
schema.properties[propName].type || '').toLowerCase().trim(),
wait = type === 'array';
if (type === 'array') {
length = prop.schema.maxItems;
if (length) {
msg = (tmp.length + 1).toString() + '/' + length.toString();
}
else {
msg = (tmp.length + 1).toString();
}
msg += delim;
raw.push(prompt.colors ? msg.grey : msg);
}
//
// Calculate the raw length and colorize the prompt
//
length = raw.join('').length;
raw[0] = raw[0];
msg = raw.join('');
if (schema.help) {
schema.help.forEach(function (line) {
logger.help(line);
});
}
//
// Emit a "prompting" event
//
prompt.emit('prompt', prop);
//
// If there is no default line, set it to an empty string
//
if(typeof defaultLine === 'undefined') {
defaultLine = '';
}
//
// set to string for readline ( will not accept Numbers )
//
defaultLine = defaultLine.toString();
//
// Make the actual read
//
read({
prompt: msg,
silent: prop.schema && prop.schema.hidden,
default: defaultLine,
input: stdin,
output: stdout
}, function (err, line) {
if (err && wait === false) {
return callback(err);
}
var against = {},
numericInput,
isValid;
if (line !== '') {
if (schema.properties[propName]) {
var type = (schema.properties[propName].type || '').toLowerCase().trim() || undefined;
//
// Attempt to parse input as a float if the schema expects a number.
//
if (type == 'number') {
numericInput = parseFloat(line, 10);
if (!isNaN(numericInput)) {
line = numericInput;
}
}
//
// Attempt to parse input as a boolean if the schema expects a boolean
//
if (type == 'boolean') {
if(line === "true") {
line = true;
}
if(line === "false") {
line = false;
}
}
//
// If the type is an array, wait for the end. Fixes #54
//
if (type == 'array') {
var length = prop.schema.maxItems;
if (err) {
if (err.message == 'canceled') {
wait = false;
stdout.write('\n');
}
}
else {
if (length) {
if (tmp.length + 1 < length) {
isValid = false;
wait = true;
}
else {
isValid = true;
wait = false;
}
}
else {
isValid = false;
wait = true;
}
tmp.push(line);
}
line = tmp;
}
}
against[propName] = line;
}
if (prop && prop.schema.before) {
line = prop.schema.before(line);
}
// Validate
if (isValid === undefined) isValid = prompt._performValidation(name, prop, against, schema, line, callback);
if (!isValid) {
return prompt.getInput(prop, callback);
}
//
// Log the resulting line, append this `property:value`
// pair to the history for `prompt` and respond to
// the callback.
//
logger.input(line.yellow);
prompt._remember(propName, line);
callback(null, line);
// Make sure `tmp` is emptied
tmp = [];
});
};
//
// ### function performValidation (name, prop, against, schema, line, callback)
// #### @name {Object} Variable name
// #### @prop {Object|string} Variable to get input for.
// #### @against {Object} Input
// #### @schema {Object} Validation schema
// #### @line {String|Boolean} Input line
// #### @callback {function} Continuation to pass control to when complete.
// Perfoms user input validation, print errors if needed and returns value according to validation
//
prompt._performValidation = function (name, prop, against, schema, line, callback) {
var numericInput, valid, msg;
try {
valid = validate(against, schema);
}
catch (err) {
return (line !== -1) ? callback(err) : false;
}
if (!valid.valid) {
msg = line !== -1 ? 'Invalid input for ' : 'Invalid command-line input for ';
if (prompt.colors) {
logger.error(msg + name.grey);
}
else {
logger.error(msg + name);
}
if (prop.schema.message) {
logger.error(prop.schema.message);
}
prompt.emit('invalid', prop, line);
}
return valid.valid;
};
//
// ### function addProperties (obj, properties, callback)
// #### @obj {Object} Object to add properties to
// #### @properties {Array} List of properties to get values for
// #### @callback {function} Continuation to pass control to when complete.
// Prompts the user for values each of the `properties` if `obj` does not already
// have a value for the property. Responds with the modified object.
//
prompt.addProperties = function (obj, properties, callback) {
properties = properties.filter(function (prop) {
return typeof obj[prop] === 'undefined';
});
if (properties.length === 0) {
return callback(obj);
}
prompt.get(properties, function (err, results) {
if (err) {
return callback(err);
}
else if (!results) {
return callback(null, obj);
}
function putNested (obj, path, value) {
var last = obj, key;
while (path.length > 1) {
key = path.shift();
if (!last[key]) {
last[key] = {};
}
last = last[key];
}
last[path.shift()] = value;
}
Object.keys(results).forEach(function (key) {
putNested(obj, key.split('.'), results[key]);
});
callback(null, obj);
});
return prompt;
};
//
// ### @private function _remember (property, value)
// #### @property {Object|string} Property that the value is in response to.
// #### @value {string} User input captured by `prompt`.
// Prepends the `property:value` pair into the private `history` Array
// for `prompt` so that it can be accessed later.
//
prompt._remember = function (property, value) {
history.unshift({
property: property,
value: value
});
//
// If the length of the `history` Array
// has exceeded the specified length to remember,
// `prompt.memory`, truncate it.
//
if (history.length > prompt.memory) {
history.splice(prompt.memory, history.length - prompt.memory);
}
};
//
// ### @private function convert (schema)
// #### @schema {Object} Schema for a property
// Converts the schema into new format if it is in old format
//
function convert(schema) {
var newProps = Object.keys(validate.messages),
newSchema = false,
key;
newProps = newProps.concat(['description', 'dependencies']);
for (key in schema) {
if (newProps.indexOf(key) > 0) {
newSchema = true;
break;
}
}
if (!newSchema || schema.validator || schema.warning || typeof schema.empty !== 'undefined') {
schema.description = schema.message;
schema.message = schema.warning;
if (typeof schema.validator === 'function') {
schema.conform = schema.validator;
} else {
schema.pattern = schema.validator;
}
if (typeof schema.empty !== 'undefined') {
schema.required = !(schema.empty);
}
delete schema.warning;
delete schema.validator;
delete schema.empty;
}
return schema;
}

View File

@ -0,0 +1,2 @@
node_modules/
npm-debug.log

View File

@ -0,0 +1,19 @@
Copyright (c) 2010 Charlie Robbins.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,86 @@
# node-pkginfo
An easy way to expose properties on a module from a package.json
## Installation
### Installing npm (node package manager)
```
curl http://npmjs.org/install.sh | sh
```
### Installing pkginfo
```
[sudo] npm install pkginfo
```
## Motivation
How often when writing node.js modules have you written the following line(s) of code?
* Hard code your version string into your code
``` js
exports.version = '0.1.0';
```
* Programmatically expose the version from the package.json
``` js
exports.version = JSON.parse(fs.readFileSync('/path/to/package.json', 'utf8')).version;
```
In other words, how often have you wanted to expose basic information from your package.json onto your module programmatically? **WELL NOW YOU CAN!**
## Usage
Using `pkginfo` is idiot-proof, just require and invoke it.
``` js
var pkginfo = require('pkginfo')(module);
console.dir(module.exports);
```
By invoking the `pkginfo` module all of the properties in your `package.json` file will be automatically exposed on the callee module (i.e. the parent module of `pkginfo`).
Here's a sample of the output:
```
{ name: 'simple-app',
description: 'A test fixture for pkginfo',
version: '0.1.0',
author: 'Charlie Robbins <charlie.robbins@gmail.com>',
keywords: [ 'test', 'fixture' ],
main: './index.js',
scripts: { test: 'vows test/*-test.js --spec' },
engines: { node: '>= 0.4.0' } }
```
### Expose specific properties
If you don't want to expose **all** properties on from your `package.json` on your module then simple pass those properties to the `pkginfo` function:
``` js
var pkginfo = require('pkginfo')(module, 'version', 'author');
console.dir(module.exports);
```
```
{ version: '0.1.0',
author: 'Charlie Robbins <charlie.robbins@gmail.com>' }
```
If you're looking for further usage see the [examples][0] included in this repository.
## Run Tests
Tests are written in [vows][1] and give complete coverage of all APIs.
```
vows test/*-test.js --spec
```
[0]: https://github.com/indexzero/node-pkginfo/tree/master/examples
[1]: http://vowsjs.org
#### Author: [Charlie Robbins](http://nodejitsu.com)
#### License: MIT

View File

@ -0,0 +1,194 @@
/*--------------------- Layout and Typography ----------------------------*/
body {
font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif;
font-size: 15px;
line-height: 22px;
color: #252519;
margin: 0; padding: 0;
}
a {
color: #261a3b;
}
a:visited {
color: #261a3b;
}
p {
margin: 0 0 15px 0;
}
h4, h5, h6 {
color: #333;
margin: 6px 0 6px 0;
font-size: 13px;
}
h2, h3 {
margin-bottom: 0;
color: #000;
}
h1 {
margin-top: 40px;
margin-bottom: 15px;
color: #000;
}
#container {
position: relative;
}
#background {
position: fixed;
top: 0; left: 525px; right: 0; bottom: 0;
background: #f5f5ff;
border-left: 1px solid #e5e5ee;
z-index: -1;
}
#jump_to, #jump_page {
background: white;
-webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777;
-webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px;
font: 10px Arial;
text-transform: uppercase;
cursor: pointer;
text-align: right;
}
#jump_to, #jump_wrapper {
position: fixed;
right: 0; top: 0;
padding: 5px 10px;
}
#jump_wrapper {
padding: 0;
display: none;
}
#jump_to:hover #jump_wrapper {
display: block;
}
#jump_page {
padding: 5px 0 3px;
margin: 0 0 25px 25px;
}
#jump_page .source {
display: block;
padding: 5px 10px;
text-decoration: none;
border-top: 1px solid #eee;
}
#jump_page .source:hover {
background: #f5f5ff;
}
#jump_page .source:first-child {
}
table td {
border: 0;
outline: 0;
}
td.docs, th.docs {
max-width: 450px;
min-width: 450px;
min-height: 5px;
padding: 10px 25px 1px 50px;
overflow-x: hidden;
vertical-align: top;
text-align: left;
}
.docs pre {
margin: 15px 0 15px;
padding-left: 15px;
}
.docs p tt, .docs p code {
background: #f8f8ff;
border: 1px solid #dedede;
font-size: 12px;
padding: 0 0.2em;
}
.pilwrap {
position: relative;
}
.pilcrow {
font: 12px Arial;
text-decoration: none;
color: #454545;
position: absolute;
top: 3px; left: -20px;
padding: 1px 2px;
opacity: 0;
-webkit-transition: opacity 0.2s linear;
}
td.docs:hover .pilcrow {
opacity: 1;
}
td.code, th.code {
padding: 14px 15px 16px 25px;
width: 100%;
vertical-align: top;
background: #f5f5ff;
border-left: 1px solid #e5e5ee;
}
pre, tt, code {
font-size: 12px; line-height: 18px;
font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace;
margin: 0; padding: 0;
}
/*---------------------- Syntax Highlighting -----------------------------*/
td.linenos { background-color: #f0f0f0; padding-right: 10px; }
span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; }
body .hll { background-color: #ffffcc }
body .c { color: #408080; font-style: italic } /* Comment */
body .err { border: 1px solid #FF0000 } /* Error */
body .k { color: #954121 } /* Keyword */
body .o { color: #666666 } /* Operator */
body .cm { color: #408080; font-style: italic } /* Comment.Multiline */
body .cp { color: #BC7A00 } /* Comment.Preproc */
body .c1 { color: #408080; font-style: italic } /* Comment.Single */
body .cs { color: #408080; font-style: italic } /* Comment.Special */
body .gd { color: #A00000 } /* Generic.Deleted */
body .ge { font-style: italic } /* Generic.Emph */
body .gr { color: #FF0000 } /* Generic.Error */
body .gh { color: #000080; font-weight: bold } /* Generic.Heading */
body .gi { color: #00A000 } /* Generic.Inserted */
body .go { color: #808080 } /* Generic.Output */
body .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
body .gs { font-weight: bold } /* Generic.Strong */
body .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
body .gt { color: #0040D0 } /* Generic.Traceback */
body .kc { color: #954121 } /* Keyword.Constant */
body .kd { color: #954121; font-weight: bold } /* Keyword.Declaration */
body .kn { color: #954121; font-weight: bold } /* Keyword.Namespace */
body .kp { color: #954121 } /* Keyword.Pseudo */
body .kr { color: #954121; font-weight: bold } /* Keyword.Reserved */
body .kt { color: #B00040 } /* Keyword.Type */
body .m { color: #666666 } /* Literal.Number */
body .s { color: #219161 } /* Literal.String */
body .na { color: #7D9029 } /* Name.Attribute */
body .nb { color: #954121 } /* Name.Builtin */
body .nc { color: #0000FF; font-weight: bold } /* Name.Class */
body .no { color: #880000 } /* Name.Constant */
body .nd { color: #AA22FF } /* Name.Decorator */
body .ni { color: #999999; font-weight: bold } /* Name.Entity */
body .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
body .nf { color: #0000FF } /* Name.Function */
body .nl { color: #A0A000 } /* Name.Label */
body .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
body .nt { color: #954121; font-weight: bold } /* Name.Tag */
body .nv { color: #19469D } /* Name.Variable */
body .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
body .w { color: #bbbbbb } /* Text.Whitespace */
body .mf { color: #666666 } /* Literal.Number.Float */
body .mh { color: #666666 } /* Literal.Number.Hex */
body .mi { color: #666666 } /* Literal.Number.Integer */
body .mo { color: #666666 } /* Literal.Number.Oct */
body .sb { color: #219161 } /* Literal.String.Backtick */
body .sc { color: #219161 } /* Literal.String.Char */
body .sd { color: #219161; font-style: italic } /* Literal.String.Doc */
body .s2 { color: #219161 } /* Literal.String.Double */
body .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
body .sh { color: #219161 } /* Literal.String.Heredoc */
body .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
body .sx { color: #954121 } /* Literal.String.Other */
body .sr { color: #BB6688 } /* Literal.String.Regex */
body .s1 { color: #219161 } /* Literal.String.Single */
body .ss { color: #19469D } /* Literal.String.Symbol */
body .bp { color: #954121 } /* Name.Builtin.Pseudo */
body .vc { color: #19469D } /* Name.Variable.Class */
body .vg { color: #19469D } /* Name.Variable.Global */
body .vi { color: #19469D } /* Name.Variable.Instance */
body .il { color: #666666 } /* Literal.Number.Integer.Long */

View File

@ -0,0 +1,101 @@
<!DOCTYPE html> <html> <head> <title>pkginfo.js</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <link rel="stylesheet" media="all" href="docco.css" /> </head> <body> <div id="container"> <div id="background"></div> <table cellpadding="0" cellspacing="0"> <thead> <tr> <th class="docs"> <h1> pkginfo.js </h1> </th> <th class="code"> </th> </tr> </thead> <tbody> <tr id="section-1"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-1">&#182;</a> </div> </td> <td class="code"> <div class="highlight"><pre><span class="cm">/*</span>
<span class="cm"> * pkginfo.js: Top-level include for the pkginfo module</span>
<span class="cm"> *</span>
<span class="cm"> * (C) 2011, Charlie Robbins</span>
<span class="cm"> *</span>
<span class="cm"> */</span>
<span class="kd">var</span> <span class="nx">fs</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;fs&#39;</span><span class="p">),</span>
<span class="nx">path</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;path&#39;</span><span class="p">);</span></pre></div> </td> </tr> <tr id="section-2"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-2">&#182;</a> </div> <h3>function pkginfo ([options, 'property', 'property' ..])</h3>
<h4>@pmodule {Module} Parent module to read from.</h4>
<h4>@options {Object|Array|string} <strong>Optional</strong> Options used when exposing properties.</h4>
<h4>@arguments {string...} <strong>Optional</strong> Specified properties to expose.</h4>
<p>Exposes properties from the package.json file for the parent module on
it's exports. Valid usage:</p>
<p><code>require('pkginfo')()</code></p>
<p><code>require('pkginfo')('version', 'author');</code></p>
<p><code>require('pkginfo')(['version', 'author']);</code></p>
<p><code>require('pkginfo')({ include: ['version', 'author'] });</code></p> </td> <td class="code"> <div class="highlight"><pre><span class="kd">var</span> <span class="nx">pkginfo</span> <span class="o">=</span> <span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">pmodule</span><span class="p">,</span> <span class="nx">options</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">args</span> <span class="o">=</span> <span class="p">[].</span><span class="nx">slice</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">arguments</span><span class="p">,</span> <span class="mi">2</span><span class="p">).</span><span class="nx">filter</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">arg</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">typeof</span> <span class="nx">arg</span> <span class="o">===</span> <span class="s1">&#39;string&#39;</span><span class="p">;</span>
<span class="p">});</span>
</pre></div> </td> </tr> <tr id="section-3"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-3">&#182;</a> </div> <p><strong>Parse variable arguments</strong></p> </td> <td class="code"> <div class="highlight"><pre> <span class="k">if</span> <span class="p">(</span><span class="nb">Array</span><span class="p">.</span><span class="nx">isArray</span><span class="p">(</span><span class="nx">options</span><span class="p">))</span> <span class="p">{</span></pre></div> </td> </tr> <tr id="section-4"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-4">&#182;</a> </div> <p>If the options passed in is an Array assume that
it is the Array of properties to expose from the
on the package.json file on the parent module.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">include</span><span class="o">:</span> <span class="nx">options</span> <span class="p">};</span>
<span class="p">}</span>
<span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">options</span> <span class="o">===</span> <span class="s1">&#39;string&#39;</span><span class="p">)</span> <span class="p">{</span></pre></div> </td> </tr> <tr id="section-5"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-5">&#182;</a> </div> <p>Otherwise if the first argument is a string, then
assume that it is the first property to expose from
the package.json file on the parent module.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">include</span><span class="o">:</span> <span class="p">[</span><span class="nx">options</span><span class="p">]</span> <span class="p">};</span>
<span class="p">}</span>
</pre></div> </td> </tr> <tr id="section-6"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-6">&#182;</a> </div> <p><strong>Setup default options</strong></p> </td> <td class="code"> <div class="highlight"><pre> <span class="nx">options</span> <span class="o">=</span> <span class="nx">options</span> <span class="o">||</span> <span class="p">{</span> <span class="nx">include</span><span class="o">:</span> <span class="p">[]</span> <span class="p">};</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">args</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span></pre></div> </td> </tr> <tr id="section-7"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-7">&#182;</a> </div> <p>If additional string arguments have been passed in
then add them to the properties to expose on the
parent module. </p> </td> <td class="code"> <div class="highlight"><pre> <span class="nx">options</span><span class="p">.</span><span class="nx">include</span> <span class="o">=</span> <span class="nx">options</span><span class="p">.</span><span class="nx">include</span><span class="p">.</span><span class="nx">concat</span><span class="p">(</span><span class="nx">args</span><span class="p">);</span>
<span class="p">}</span>
<span class="kd">var</span> <span class="nx">pkg</span> <span class="o">=</span> <span class="nx">pkginfo</span><span class="p">.</span><span class="nx">read</span><span class="p">(</span><span class="nx">pmodule</span><span class="p">,</span> <span class="nx">options</span><span class="p">.</span><span class="nx">dir</span><span class="p">).</span><span class="kr">package</span><span class="p">;</span>
<span class="nb">Object</span><span class="p">.</span><span class="nx">keys</span><span class="p">(</span><span class="nx">pkg</span><span class="p">).</span><span class="nx">forEach</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">key</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">options</span><span class="p">.</span><span class="nx">include</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="o">!~</span><span class="nx">options</span><span class="p">.</span><span class="nx">include</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="nx">key</span><span class="p">))</span> <span class="p">{</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">pmodule</span><span class="p">.</span><span class="nx">exports</span><span class="p">[</span><span class="nx">key</span><span class="p">])</span> <span class="p">{</span>
<span class="nx">pmodule</span><span class="p">.</span><span class="nx">exports</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span> <span class="o">=</span> <span class="nx">pkg</span><span class="p">[</span><span class="nx">key</span><span class="p">];</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="k">return</span> <span class="nx">pkginfo</span><span class="p">;</span>
<span class="p">};</span></pre></div> </td> </tr> <tr id="section-8"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-8">&#182;</a> </div> <h3>function find (dir)</h3>
<h4>@pmodule {Module} Parent module to read from.</h4>
<h4>@dir {string} <strong>Optional</strong> Directory to start search from.</h4>
<p>Searches up the directory tree from <code>dir</code> until it finds a directory
which contains a <code>package.json</code> file. </p> </td> <td class="code"> <div class="highlight"><pre><span class="nx">pkginfo</span><span class="p">.</span><span class="nx">find</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">pmodule</span><span class="p">,</span> <span class="nx">dir</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">dir</span> <span class="o">=</span> <span class="nx">dir</span> <span class="o">||</span> <span class="nx">pmodule</span><span class="p">.</span><span class="nx">filename</span><span class="p">;</span>
<span class="nx">dir</span> <span class="o">=</span> <span class="nx">path</span><span class="p">.</span><span class="nx">dirname</span><span class="p">(</span><span class="nx">dir</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">files</span> <span class="o">=</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">readdirSync</span><span class="p">(</span><span class="nx">dir</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="o">~</span><span class="nx">files</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="s1">&#39;package.json&#39;</span><span class="p">))</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">dir</span><span class="p">,</span> <span class="s1">&#39;package.json&#39;</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">dir</span> <span class="o">===</span> <span class="s1">&#39;/&#39;</span><span class="p">)</span> <span class="p">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s1">&#39;Could not find package.json up from: &#39;</span> <span class="o">+</span> <span class="nx">dir</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nx">pkginfo</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="nx">dir</span><span class="p">);</span>
<span class="p">};</span></pre></div> </td> </tr> <tr id="section-9"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-9">&#182;</a> </div> <h3>function read (pmodule, dir)</h3>
<h4>@pmodule {Module} Parent module to read from.</h4>
<h4>@dir {string} <strong>Optional</strong> Directory to start search from.</h4>
<p>Searches up the directory tree from <code>dir</code> until it finds a directory
which contains a <code>package.json</code> file and returns the package information.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nx">pkginfo</span><span class="p">.</span><span class="nx">read</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">pmodule</span><span class="p">,</span> <span class="nx">dir</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">dir</span> <span class="o">=</span> <span class="nx">pkginfo</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="nx">pmodule</span><span class="p">,</span> <span class="nx">dir</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">data</span> <span class="o">=</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">readFileSync</span><span class="p">(</span><span class="nx">dir</span><span class="p">).</span><span class="nx">toString</span><span class="p">();</span>
<span class="k">return</span> <span class="p">{</span>
<span class="nx">dir</span><span class="o">:</span> <span class="nx">dir</span><span class="p">,</span>
<span class="kr">package</span><span class="o">:</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span>
<span class="p">};</span>
<span class="p">};</span></pre></div> </td> </tr> <tr id="section-10"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-10">&#182;</a> </div> <p>Call <code>pkginfo</code> on this module and expose version.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nx">pkginfo</span><span class="p">(</span><span class="nx">module</span><span class="p">,</span> <span class="p">{</span>
<span class="nx">dir</span><span class="o">:</span> <span class="nx">__dirname</span><span class="p">,</span>
<span class="nx">include</span><span class="o">:</span> <span class="p">[</span><span class="s1">&#39;version&#39;</span><span class="p">],</span>
<span class="nx">target</span><span class="o">:</span> <span class="nx">pkginfo</span>
<span class="p">});</span>
</pre></div> </td> </tr> </tbody> </table> </div> </body> </html>

View File

@ -0,0 +1,19 @@
/*
* all-properties.js: Sample of including all properties from a package.json file
*
* (C) 2011, Charlie Robbins
*
*/
var util = require('util'),
pkginfo = require('../lib/pkginfo')(module);
exports.someFunction = function () {
console.log('some of your custom logic here');
};
console.log('Inspecting module:');
console.dir(module.exports);
console.log('\nAll exports exposed:');
console.error(Object.keys(module.exports));

View File

@ -0,0 +1,20 @@
/*
* array-argument.js: Sample of including specific properties from a package.json file
* using Array argument syntax.
*
* (C) 2011, Charlie Robbins
*
*/
var util = require('util'),
pkginfo = require('../lib/pkginfo')(module, ['version', 'author']);
exports.someFunction = function () {
console.log('some of your custom logic here');
};
console.log('Inspecting module:');
console.dir(module.exports);
console.log('\nAll exports exposed:');
console.error(Object.keys(module.exports));

View File

@ -0,0 +1,19 @@
/*
* multiple-properties.js: Sample of including multiple properties from a package.json file
*
* (C) 2011, Charlie Robbins
*
*/
var util = require('util'),
pkginfo = require('../lib/pkginfo')(module, 'version', 'author');
exports.someFunction = function () {
console.log('some of your custom logic here');
};
console.log('Inspecting module:');
console.dir(module.exports);
console.log('\nAll exports exposed:');
console.error(Object.keys(module.exports));

View File

@ -0,0 +1,22 @@
/*
* object-argument.js: Sample of including specific properties from a package.json file
* using Object argument syntax.
*
* (C) 2011, Charlie Robbins
*
*/
var util = require('util'),
pkginfo = require('../lib/pkginfo')(module, {
include: ['version', 'author']
});
exports.someFunction = function () {
console.log('some of your custom logic here');
};
console.log('Inspecting module:');
console.dir(module.exports);
console.log('\nAll exports exposed:');
console.error(Object.keys(module.exports));

View File

@ -0,0 +1,10 @@
{
"name": "simple-app",
"description": "A test fixture for pkginfo",
"version": "0.1.0",
"author": "Charlie Robbins <charlie.robbins@gmail.com>",
"keywords": ["test", "fixture"],
"main": "./index.js",
"scripts": { "test": "vows test/*-test.js --spec" },
"engines": { "node": ">= 0.4.0" }
}

View File

@ -0,0 +1,19 @@
/*
* single-property.js: Sample of including a single specific properties from a package.json file
*
* (C) 2011, Charlie Robbins
*
*/
var util = require('util'),
pkginfo = require('../lib/pkginfo')(module, 'version');
exports.someFunction = function () {
console.log('some of your custom logic here');
};
console.log('Inspecting module:');
console.dir(module.exports);
console.log('\nAll exports exposed:');
console.error(Object.keys(module.exports));

View File

@ -0,0 +1,11 @@
{
"name": "simple-app-subdir",
"description": "A test fixture for pkginfo",
"version": "0.1.0",
"author": "Charlie Robbins <charlie.robbins@gmail.com>",
"keywords": ["test", "fixture"],
"main": "./index.js",
"scripts": { "test": "vows test/*-test.js --spec" },
"engines": { "node": ">= 0.4.0" },
"subdironly": "true"
}

View File

@ -0,0 +1,20 @@
/*
* multiple-properties.js: Sample of including multiple properties from a package.json file
*
* (C) 2011, Charlie Robbins
*
*/
var util = require('util'),
path = require('path'),
pkginfo = require('../lib/pkginfo')(module, { dir: path.resolve(__dirname, 'subdir' )});
exports.someFunction = function () {
console.log('some of your custom logic here');
};
console.log('Inspecting module:');
console.dir(module.exports);
console.log('\nAll exports exposed:');
console.error(Object.keys(module.exports));

View File

@ -0,0 +1,136 @@
/*
* pkginfo.js: Top-level include for the pkginfo module
*
* (C) 2011, Charlie Robbins
*
*/
var fs = require('fs'),
path = require('path');
//
// ### function pkginfo ([options, 'property', 'property' ..])
// #### @pmodule {Module} Parent module to read from.
// #### @options {Object|Array|string} **Optional** Options used when exposing properties.
// #### @arguments {string...} **Optional** Specified properties to expose.
// Exposes properties from the package.json file for the parent module on
// it's exports. Valid usage:
//
// `require('pkginfo')()`
//
// `require('pkginfo')('version', 'author');`
//
// `require('pkginfo')(['version', 'author']);`
//
// `require('pkginfo')({ include: ['version', 'author'] });`
//
var pkginfo = module.exports = function (pmodule, options) {
var args = [].slice.call(arguments, 2).filter(function (arg) {
return typeof arg === 'string';
});
//
// **Parse variable arguments**
//
if (Array.isArray(options)) {
//
// If the options passed in is an Array assume that
// it is the Array of properties to expose from the
// on the package.json file on the parent module.
//
options = { include: options };
}
else if (typeof options === 'string') {
//
// Otherwise if the first argument is a string, then
// assume that it is the first property to expose from
// the package.json file on the parent module.
//
options = { include: [options] };
}
//
// **Setup default options**
//
options = options || {};
// ensure that includes have been defined
options.include = options.include || [];
if (args.length > 0) {
//
// If additional string arguments have been passed in
// then add them to the properties to expose on the
// parent module.
//
options.include = options.include.concat(args);
}
var pkg = pkginfo.read(pmodule, options.dir).package;
Object.keys(pkg).forEach(function (key) {
if (options.include.length > 0 && !~options.include.indexOf(key)) {
return;
}
if (!pmodule.exports[key]) {
pmodule.exports[key] = pkg[key];
}
});
return pkginfo;
};
//
// ### function find (dir)
// #### @pmodule {Module} Parent module to read from.
// #### @dir {string} **Optional** Directory to start search from.
// Searches up the directory tree from `dir` until it finds a directory
// which contains a `package.json` file.
//
pkginfo.find = function (pmodule, dir) {
if (! dir) {
dir = path.dirname(pmodule.filename);
}
var files = fs.readdirSync(dir);
if (~files.indexOf('package.json')) {
return path.join(dir, 'package.json');
}
if (dir === '/') {
throw new Error('Could not find package.json up from: ' + dir);
}
else if (!dir || dir === '.') {
throw new Error('Cannot find package.json from unspecified directory');
}
return pkginfo.find(pmodule, path.dirname(dir));
};
//
// ### function read (pmodule, dir)
// #### @pmodule {Module} Parent module to read from.
// #### @dir {string} **Optional** Directory to start search from.
// Searches up the directory tree from `dir` until it finds a directory
// which contains a `package.json` file and returns the package information.
//
pkginfo.read = function (pmodule, dir) {
dir = pkginfo.find(pmodule, dir);
var data = fs.readFileSync(dir).toString();
return {
dir: dir,
package: JSON.parse(data)
};
};
//
// Call `pkginfo` on this module and expose version.
//
pkginfo(module, {
dir: __dirname,
include: ['version'],
target: pkginfo
});

View File

@ -0,0 +1,54 @@
{
"name": "pkginfo",
"version": "0.3.0",
"description": "An easy way to expose properties on a module from a package.json",
"author": {
"name": "Charlie Robbins",
"email": "charlie.robbins@gmail.com"
},
"repository": {
"type": "git",
"url": "http://github.com/indexzero/node-pkginfo.git"
},
"keywords": [
"info",
"tools",
"package.json"
],
"devDependencies": {
"vows": "0.7.x"
},
"main": "./lib/pkginfo",
"scripts": {
"test": "vows test/*-test.js --spec"
},
"engines": {
"node": ">= 0.4.0"
},
"readme": "# node-pkginfo\n\nAn easy way to expose properties on a module from a package.json\n\n## Installation\n\n### Installing npm (node package manager)\n```\n curl http://npmjs.org/install.sh | sh\n```\n\n### Installing pkginfo\n```\n [sudo] npm install pkginfo\n```\n\n## Motivation\nHow often when writing node.js modules have you written the following line(s) of code? \n\n* Hard code your version string into your code\n\n``` js\n exports.version = '0.1.0';\n```\n\n* Programmatically expose the version from the package.json\n\n``` js\n exports.version = JSON.parse(fs.readFileSync('/path/to/package.json', 'utf8')).version;\n```\n\nIn other words, how often have you wanted to expose basic information from your package.json onto your module programmatically? **WELL NOW YOU CAN!**\n\n## Usage\n\nUsing `pkginfo` is idiot-proof, just require and invoke it. \n\n``` js\n var pkginfo = require('pkginfo')(module);\n \n console.dir(module.exports);\n```\n\nBy invoking the `pkginfo` module all of the properties in your `package.json` file will be automatically exposed on the callee module (i.e. the parent module of `pkginfo`). \n\nHere's a sample of the output:\n\n```\n { name: 'simple-app',\n description: 'A test fixture for pkginfo',\n version: '0.1.0',\n author: 'Charlie Robbins <charlie.robbins@gmail.com>',\n keywords: [ 'test', 'fixture' ],\n main: './index.js',\n scripts: { test: 'vows test/*-test.js --spec' },\n engines: { node: '>= 0.4.0' } }\n```\n\n### Expose specific properties\nIf you don't want to expose **all** properties on from your `package.json` on your module then simple pass those properties to the `pkginfo` function:\n\n``` js\n var pkginfo = require('pkginfo')(module, 'version', 'author');\n \n console.dir(module.exports);\n```\n\n```\n { version: '0.1.0',\n author: 'Charlie Robbins <charlie.robbins@gmail.com>' }\n```\n\nIf you're looking for further usage see the [examples][0] included in this repository. \n\n## Run Tests\nTests are written in [vows][1] and give complete coverage of all APIs.\n\n```\n vows test/*-test.js --spec\n```\n\n[0]: https://github.com/indexzero/node-pkginfo/tree/master/examples\n[1]: http://vowsjs.org\n\n#### Author: [Charlie Robbins](http://nodejitsu.com)\n#### License: MIT",
"readmeFilename": "README.md",
"_id": "pkginfo@0.3.0",
"dist": {
"shasum": "726411401039fe9b009eea86614295d5f3a54276",
"tarball": "http://registry.npmjs.org/pkginfo/-/pkginfo-0.3.0.tgz"
},
"_npmVersion": "1.1.66",
"_npmUser": {
"name": "indexzero",
"email": "charlie.robbins@gmail.com"
},
"maintainers": [
{
"name": "indexzero",
"email": "charlie.robbins@gmail.com"
}
],
"directories": {},
"_shasum": "726411401039fe9b009eea86614295d5f3a54276",
"_from": "pkginfo@0.x.x",
"_resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.0.tgz",
"bugs": {
"url": "https://github.com/indexzero/node-pkginfo/issues"
},
"homepage": "https://github.com/indexzero/node-pkginfo"
}

View File

@ -0,0 +1,83 @@
/*
* pkginfo-test.js: Tests for the pkginfo module.
*
* (C) 2011, Charlie Robbins
*
*/
var assert = require('assert'),
exec = require('child_process').exec,
fs = require('fs'),
path = require('path'),
vows = require('vows'),
pkginfo = require('../lib/pkginfo');
function assertProperties (source, target) {
assert.lengthOf(source, target.length + 1);
target.forEach(function (prop) {
assert.isTrue(!!~source.indexOf(prop));
});
}
function compareWithExample(targetPath) {
var examplePaths = ['package.json'];
if (targetPath) {
examplePaths.unshift(targetPath);
}
return function(exposed) {
var pkg = fs.readFileSync(path.join.apply(null, [__dirname, '..', 'examples'].concat(examplePaths))).toString(),
keys = Object.keys(JSON.parse(pkg));
assertProperties(exposed, keys);
};
}
function testExposes (options) {
return {
topic: function () {
exec('node ' + path.join(__dirname, '..', 'examples', options.script), this.callback);
},
"should expose that property correctly": function (err, stdout, stderr) {
assert.isNull(err);
var exposed = stderr.match(/'(\w+)'/ig).map(function (p) {
return p.substring(1, p.length - 1);
});
return !options.assert
? assertProperties(exposed, options.properties)
: options.assert(exposed);
}
}
}
vows.describe('pkginfo').addBatch({
"When using the pkginfo module": {
"and passed a single `string` argument": testExposes({
script: 'single-property.js',
properties: ['version']
}),
"and passed multiple `string` arguments": testExposes({
script: 'multiple-properties.js',
properties: ['version', 'author']
}),
"and passed an `object` argument": testExposes({
script: 'object-argument.js',
properties: ['version', 'author']
}),
"and passed an `array` argument": testExposes({
script: 'array-argument.js',
properties: ['version', 'author']
}),
"and read from a specified directory": testExposes({
script: 'target-dir.js',
assert: compareWithExample('subdir')
}),
"and passed no arguments": testExposes({
script: 'all-properties.js',
assert: compareWithExample()
})
}
}).export(module);

View File

@ -0,0 +1,2 @@
npm-debug.log
node_modules

View File

@ -0,0 +1,25 @@
Copyright (c) Isaac Z. Schlueter
All rights reserved.
The BSD License
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,15 @@
The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@ -0,0 +1,53 @@
## read
For reading user input from stdin.
Similar to the `readline` builtin's `question()` method, but with a
few more features.
## USAGE
```javascript
var read = require("read")
read(options, callback)
```
The callback gets called with either the user input, or the default
specified, or an error, as `callback(error, result, isDefault)`
node style.
## OPTIONS
Every option is optional.
* `prompt` What to write to stdout before reading input.
* `silent` Don't echo the output as the user types it.
* `replace` Replace silenced characters with the supplied character value.
* `timeout` Number of ms to wait for user input before giving up.
* `default` The default value if the user enters nothing.
* `edit` Allow the user to edit the default value.
* `terminal` Treat the output as a TTY, whether it is or not.
* `input` Readable stream to get input data from. (default `process.stdin`)
* `output` Writeable stream to write prompts to. (default: `process.stdout`)
If silent is true, and the input is a TTY, then read will set raw
mode, and read character by character.
## COMPATIBILITY
This module works sort of with node 0.6. It does not work with node
versions less than 0.6. It is best on node 0.8.
On node version 0.6, it will remove all listeners on the input
stream's `data` and `keypress` events, because the readline module did
not fully clean up after itself in that version of node, and did not
make it possible to clean up after it in a way that has no potential
for side effects.
Additionally, some of the readline options (like `terminal`) will not
function in versions of node before 0.8, because they were not
implemented in the builtin readline module.
## CONTRIBUTING
Patches welcome.

View File

@ -0,0 +1,13 @@
var read = require("../lib/read.js")
read({prompt: "Username: ", default: "test-user" }, function (er, user) {
read({prompt: "Password: ", default: "test-pass", silent: true }, function (er, pass) {
read({prompt: "Password again: ", default: "test-pass", silent: true }, function (er, pass2) {
console.error({user: user,
pass: pass,
verify: pass2,
passMatch: (pass === pass2)})
console.error("the program should exit now")
})
})
})

View File

@ -0,0 +1,113 @@
module.exports = read
var readline = require('readline')
var Mute = require('mute-stream')
function read (opts, cb) {
if (opts.num) {
throw new Error('read() no longer accepts a char number limit')
}
if (typeof opts.default !== 'undefined' &&
typeof opts.default !== 'string' &&
typeof opts.default !== 'number') {
throw new Error('default value must be string or number')
}
var input = opts.input || process.stdin
var output = opts.output || process.stdout
var prompt = (opts.prompt || '').trim() + ' '
var silent = opts.silent
var editDef = false
var timeout = opts.timeout
var def = opts.default || ''
if (def) {
if (silent) {
prompt += '(<default hidden>) '
} else if (opts.edit) {
editDef = true
} else {
prompt += '(' + def + ') '
}
}
var terminal = !!(opts.terminal || output.isTTY)
var m = new Mute({ replace: opts.replace, prompt: prompt })
m.pipe(output, {end: false})
output = m
var rlOpts = { input: input, output: output, terminal: terminal }
if (process.version.match(/^v0\.6/)) {
var rl = readline.createInterface(rlOpts.input, rlOpts.output)
} else {
var rl = readline.createInterface(rlOpts)
}
output.unmute()
rl.setPrompt(prompt)
rl.prompt()
if (silent) {
output.mute()
} else if (editDef) {
rl.line = def
rl.cursor = def.length
rl._refreshLine()
}
var called = false
rl.on('line', onLine)
rl.on('error', onError)
rl.on('SIGINT', function () {
rl.close()
onError(new Error('canceled'))
})
var timer
if (timeout) {
timer = setTimeout(function () {
onError(new Error('timed out'))
}, timeout)
}
function done () {
called = true
rl.close()
if (process.version.match(/^v0\.6/)) {
rl.input.removeAllListeners('data')
rl.input.removeAllListeners('keypress')
rl.input.pause()
}
clearTimeout(timer)
output.mute()
output.end()
}
function onError (er) {
if (called) return
done()
return cb(er)
}
function onLine (line) {
if (called) return
if (silent && terminal) {
output.unmute()
output.write('\r\n')
}
done()
// truncate the \n at the end.
line = line.replace(/\r?\n$/, '')
var isDefault = !!(editDef && line === def)
if (def && !line) {
isDefault = true
line = def
}
cb(null, line, isDefault)
}
}

View File

@ -0,0 +1,15 @@
The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@ -0,0 +1,68 @@
# mute-stream
Bytes go in, but they don't come out (when muted).
This is a basic pass-through stream, but when muted, the bytes are
silently dropped, rather than being passed through.
## Usage
```javascript
var MuteStream = require('mute-stream')
var ms = new MuteStream(options)
ms.pipe(process.stdout)
ms.write('foo') // writes 'foo' to stdout
ms.mute()
ms.write('bar') // does not write 'bar'
ms.unmute()
ms.write('baz') // writes 'baz' to stdout
// can also be used to mute incoming data
var ms = new MuteStream
input.pipe(ms)
ms.on('data', function (c) {
console.log('data: ' + c)
})
input.emit('data', 'foo') // logs 'foo'
ms.mute()
input.emit('data', 'bar') // does not log 'bar'
ms.unmute()
input.emit('data', 'baz') // logs 'baz'
```
## Options
All options are optional.
* `replace` Set to a string to replace each character with the
specified string when muted. (So you can show `****` instead of the
password, for example.)
* `prompt` If you are using a replacement char, and also using a
prompt with a readline stream (as for a `Password: *****` input),
then specify what the prompt is so that backspace will work
properly. Otherwise, pressing backspace will overwrite the prompt
with the replacement character, which is weird.
## ms.mute()
Set `muted` to `true`. Turns `.write()` into a no-op.
## ms.unmute()
Set `muted` to `false`
## ms.isTTY
True if the pipe destination is a TTY, or if the incoming pipe source is
a TTY.
## Other stream methods...
The other standard readable and writable stream methods are all
available. The MuteStream object acts as a facade to its pipe source
and destination.

View File

@ -0,0 +1,140 @@
var Stream = require('stream')
module.exports = MuteStream
// var out = new MuteStream(process.stdout)
// argument auto-pipes
function MuteStream (opts) {
Stream.apply(this)
opts = opts || {}
this.writable = this.readable = true
this.muted = false
this.on('pipe', this._onpipe)
this.replace = opts.replace
// For readline-type situations
// This much at the start of a line being redrawn after a ctrl char
// is seen (such as backspace) won't be redrawn as the replacement
this._prompt = opts.prompt || null
this._hadControl = false
}
MuteStream.prototype = Object.create(Stream.prototype)
Object.defineProperty(MuteStream.prototype, 'constructor', {
value: MuteStream,
enumerable: false
})
MuteStream.prototype.mute = function () {
this.muted = true
}
MuteStream.prototype.unmute = function () {
this.muted = false
}
Object.defineProperty(MuteStream.prototype, '_onpipe', {
value: onPipe,
enumerable: false,
writable: true,
configurable: true
})
function onPipe (src) {
this._src = src
}
Object.defineProperty(MuteStream.prototype, 'isTTY', {
get: getIsTTY,
set: setIsTTY,
enumerable: true,
configurable: true
})
function getIsTTY () {
return( (this._dest) ? this._dest.isTTY
: (this._src) ? this._src.isTTY
: false
)
}
// basically just get replace the getter/setter with a regular value
function setIsTTY (isTTY) {
Object.defineProperty(this, 'isTTY', {
value: isTTY,
enumerable: true,
writable: true,
configurable: true
})
}
Object.defineProperty(MuteStream.prototype, 'rows', {
get: function () {
return( this._dest ? this._dest.rows
: this._src ? this._src.rows
: undefined )
}, enumerable: true, configurable: true })
Object.defineProperty(MuteStream.prototype, 'columns', {
get: function () {
return( this._dest ? this._dest.columns
: this._src ? this._src.columns
: undefined )
}, enumerable: true, configurable: true })
MuteStream.prototype.pipe = function (dest) {
this._dest = dest
return Stream.prototype.pipe.call(this, dest)
}
MuteStream.prototype.pause = function () {
if (this._src) return this._src.pause()
}
MuteStream.prototype.resume = function () {
if (this._src) return this._src.resume()
}
MuteStream.prototype.write = function (c) {
if (this.muted) {
if (!this.replace) return true
if (c.match(/^\u001b/)) {
this._hadControl = true
return this.emit('data', c)
} else {
if (this._prompt && this._hadControl &&
c.indexOf(this._prompt) === 0) {
this._hadControl = false
this.emit('data', this._prompt)
c = c.substr(this._prompt.length)
}
c = c.toString().replace(/./g, this.replace)
}
}
this.emit('data', c)
}
MuteStream.prototype.end = function (c) {
if (this.muted) {
if (c && this.replace) {
c = c.toString().replace(/./g, this.replace)
} else {
c = null
}
}
if (c) this.emit('data', c)
this.emit('end')
}
function proxy (fn) { return function () {
var d = this._dest
var s = this._src
if (d && d[fn]) d[fn].apply(d, arguments)
if (s && s[fn]) s[fn].apply(s, arguments)
}}
MuteStream.prototype.destroy = proxy('destroy')
MuteStream.prototype.destroySoon = proxy('destroySoon')
MuteStream.prototype.close = proxy('close')

View File

@ -0,0 +1,56 @@
{
"name": "mute-stream",
"version": "0.0.5",
"main": "mute.js",
"directories": {
"test": "test"
},
"devDependencies": {
"tap": "~0.2.5"
},
"scripts": {
"test": "tap test/*.js"
},
"repository": {
"type": "git",
"url": "git://github.com/isaacs/mute-stream.git"
},
"keywords": [
"mute",
"stream",
"pipe"
],
"author": {
"name": "Isaac Z. Schlueter",
"email": "i@izs.me",
"url": "http://blog.izs.me/"
},
"license": "ISC",
"description": "Bytes go in, but they don't come out (when muted).",
"gitHead": "17d9854a315f56088d039534f87b740e470a9af0",
"bugs": {
"url": "https://github.com/isaacs/mute-stream/issues"
},
"homepage": "https://github.com/isaacs/mute-stream#readme",
"_id": "mute-stream@0.0.5",
"_shasum": "8fbfabb0a98a253d3184331f9e8deb7372fac6c0",
"_from": "mute-stream@~0.0.4",
"_npmVersion": "2.10.0",
"_nodeVersion": "2.0.1",
"_npmUser": {
"name": "isaacs",
"email": "isaacs@npmjs.com"
},
"dist": {
"shasum": "8fbfabb0a98a253d3184331f9e8deb7372fac6c0",
"tarball": "http://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz"
},
"maintainers": [
{
"name": "isaacs",
"email": "i@izs.me"
}
],
"_resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz",
"readme": "ERROR: No README data found!"
}

View File

@ -0,0 +1,207 @@
var Stream = require('stream')
var tap = require('tap')
var MS = require('../mute.js')
// some marker objects
var END = {}
var PAUSE = {}
var RESUME = {}
function PassThrough () {
Stream.call(this)
this.readable = this.writable = true
}
PassThrough.prototype = Object.create(Stream.prototype, {
constructor: {
value: PassThrough
},
write: {
value: function (c) {
this.emit('data', c)
return true
}
},
end: {
value: function (c) {
if (c) this.write(c)
this.emit('end')
}
},
pause: {
value: function () {
this.emit('pause')
}
},
resume: {
value: function () {
this.emit('resume')
}
}
})
tap.test('incoming', function (t) {
var ms = new MS
var str = new PassThrough
str.pipe(ms)
var expect = ['foo', 'boo', END]
ms.on('data', function (c) {
t.equal(c, expect.shift())
})
ms.on('end', function () {
t.equal(END, expect.shift())
t.end()
})
str.write('foo')
ms.mute()
str.write('bar')
ms.unmute()
str.write('boo')
ms.mute()
str.write('blaz')
str.end('grelb')
})
tap.test('outgoing', function (t) {
var ms = new MS
var str = new PassThrough
ms.pipe(str)
var expect = ['foo', 'boo', END]
str.on('data', function (c) {
t.equal(c, expect.shift())
})
str.on('end', function () {
t.equal(END, expect.shift())
t.end()
})
ms.write('foo')
ms.mute()
ms.write('bar')
ms.unmute()
ms.write('boo')
ms.mute()
ms.write('blaz')
ms.end('grelb')
})
tap.test('isTTY', function (t) {
var str = new PassThrough
str.isTTY = true
str.columns=80
str.rows=24
var ms = new MS
t.equal(ms.isTTY, false)
t.equal(ms.columns, undefined)
t.equal(ms.rows, undefined)
ms.pipe(str)
t.equal(ms.isTTY, true)
t.equal(ms.columns, 80)
t.equal(ms.rows, 24)
str.isTTY = false
t.equal(ms.isTTY, false)
t.equal(ms.columns, 80)
t.equal(ms.rows, 24)
str.isTTY = true
t.equal(ms.isTTY, true)
t.equal(ms.columns, 80)
t.equal(ms.rows, 24)
ms.isTTY = false
t.equal(ms.isTTY, false)
t.equal(ms.columns, 80)
t.equal(ms.rows, 24)
ms = new MS
t.equal(ms.isTTY, false)
str.pipe(ms)
t.equal(ms.isTTY, true)
str.isTTY = false
t.equal(ms.isTTY, false)
str.isTTY = true
t.equal(ms.isTTY, true)
ms.isTTY = false
t.equal(ms.isTTY, false)
t.end()
})
tap.test('pause/resume incoming', function (t) {
var str = new PassThrough
var ms = new MS
str.on('pause', function () {
t.equal(PAUSE, expect.shift())
})
str.on('resume', function () {
t.equal(RESUME, expect.shift())
})
var expect = [PAUSE, RESUME, PAUSE, RESUME]
str.pipe(ms)
ms.pause()
ms.resume()
ms.pause()
ms.resume()
t.equal(expect.length, 0, 'saw all events')
t.end()
})
tap.test('replace with *', function (t) {
var str = new PassThrough
var ms = new MS({replace: '*'})
str.pipe(ms)
var expect = ['foo', '*****', 'bar', '***', 'baz', 'boo', '**', '****']
ms.on('data', function (c) {
t.equal(c, expect.shift())
})
str.write('foo')
ms.mute()
str.write('12345')
ms.unmute()
str.write('bar')
ms.mute()
str.write('baz')
ms.unmute()
str.write('baz')
str.write('boo')
ms.mute()
str.write('xy')
str.write('xyzΩ')
t.equal(expect.length, 0)
t.end()
})
tap.test('replace with ~YARG~', function (t) {
var str = new PassThrough
var ms = new MS({replace: '~YARG~'})
str.pipe(ms)
var expect = ['foo', '~YARG~~YARG~~YARG~~YARG~~YARG~', 'bar',
'~YARG~~YARG~~YARG~', 'baz', 'boo', '~YARG~~YARG~',
'~YARG~~YARG~~YARG~~YARG~']
ms.on('data', function (c) {
t.equal(c, expect.shift())
})
// also throw some unicode in there, just for good measure.
str.write('foo')
ms.mute()
str.write('ΩΩ')
ms.unmute()
str.write('bar')
ms.mute()
str.write('Ω')
ms.unmute()
str.write('baz')
str.write('boo')
ms.mute()
str.write('Ω')
str.write('ΩΩ')
t.equal(expect.length, 0)
t.end()
})

View File

@ -0,0 +1,55 @@
{
"name": "read",
"version": "1.0.6",
"main": "lib/read.js",
"dependencies": {
"mute-stream": "~0.0.4"
},
"devDependencies": {
"tap": "*"
},
"engines": {
"node": ">=0.8"
},
"author": {
"name": "Isaac Z. Schlueter",
"email": "i@izs.me",
"url": "http://blog.izs.me/"
},
"description": "read(1) for node programs",
"repository": {
"type": "git",
"url": "git://github.com/isaacs/read.git"
},
"license": "ISC",
"scripts": {
"test": "tap test/*.js"
},
"gitHead": "2f5101c8e41332a033e5aa4e27e33fd6e09598e2",
"bugs": {
"url": "https://github.com/isaacs/read/issues"
},
"homepage": "https://github.com/isaacs/read#readme",
"_id": "read@1.0.6",
"_shasum": "09873c14ecc114d063fad43b8ca5a33d304721c8",
"_from": "read@1.0.x",
"_npmVersion": "2.10.0",
"_nodeVersion": "2.0.1",
"_npmUser": {
"name": "isaacs",
"email": "isaacs@npmjs.com"
},
"dist": {
"shasum": "09873c14ecc114d063fad43b8ca5a33d304721c8",
"tarball": "http://registry.npmjs.org/read/-/read-1.0.6.tgz"
},
"maintainers": [
{
"name": "isaacs",
"email": "i@izs.me"
}
],
"directories": {},
"_resolved": "https://registry.npmjs.org/read/-/read-1.0.6.tgz",
"readme": "ERROR: No README data found!"
}

View File

@ -0,0 +1,4 @@
var read = require('read');
read({ silent: true, prompt: 'stars: ' }, function(er, data) {
console.log(er, data)
})

View File

@ -0,0 +1,60 @@
var tap = require('tap')
var read = require('../lib/read.js')
if (process.argv[2] === 'child') {
return child()
}
var CLOSE = 'close'
if (process.version.match(/^v0\.6/)) {
CLOSE = 'exit'
}
var spawn = require('child_process').spawn
tap.test('basic', function (t) {
var child = spawn(process.execPath, [__filename, 'child'])
var output = ''
var write = child.stdin.write.bind(child.stdin)
child.stdout.on('data', function (c) {
console.error('data %s', c)
output += c
if (output.match(/Username: \(test-user\) $/)) {
process.nextTick(write.bind(null, 'a user\n'))
} else if (output.match(/Password: \(<default hidden>\) $/)) {
process.nextTick(write.bind(null, 'a password\n'))
} else if (output.match(/Password again: \(<default hidden>\) $/)) {
process.nextTick(write.bind(null, 'a password\n'))
} else {
console.error('prompts done, output=%j', output)
}
})
var result = ''
child.stderr.on('data', function (c) {
result += c
console.error('result %j', c.toString())
})
child.on(CLOSE, function () {
result = JSON.parse(result)
t.same(result, {"user":"a user","pass":"a password","verify":"a password","passMatch":true})
t.equal(output, 'Username: (test-user) Password: (<default hidden>) Password again: (<default hidden>) ')
t.end()
})
})
function child () {
read({prompt: "Username: ", default: "test-user" }, function (er, user) {
read({prompt: "Password: ", default: "test-pass", silent: true }, function (er, pass) {
read({prompt: "Password again: ", default: "test-pass", silent: true }, function (er, pass2) {
console.error(JSON.stringify({user: user,
pass: pass,
verify: pass2,
passMatch: (pass === pass2)}))
if (process.stdin.unref)
process.stdin.unref()
})
})
})
}

View File

@ -0,0 +1,60 @@
var tap = require('tap')
var read = require('../lib/read.js')
if (process.argv[2] === 'child') {
return child()
}
var CLOSE = 'close'
if (process.version.match(/^v0\.6/)) {
CLOSE = 'exit'
}
var spawn = require('child_process').spawn
tap.test('defaults', function (t) {
var child = spawn(process.execPath, [__filename, 'child'])
var output = ''
var write = child.stdin.write.bind(child.stdin)
child.stdout.on('data', function (c) {
console.error('data %s', c)
output += c
if (output.match(/Username: \(test-user\) $/)) {
process.nextTick(write.bind(null, '\n'))
} else if (output.match(/Password: \(<default hidden>\) $/)) {
process.nextTick(write.bind(null, '\n'))
} else if (output.match(/Password again: \(<default hidden>\) $/)) {
process.nextTick(write.bind(null, '\n'))
} else {
console.error('prompts done, output=%j', output)
}
})
var result = ''
child.stderr.on('data', function (c) {
result += c
console.error('result %j', c.toString())
})
child.on(CLOSE, function () {
result = JSON.parse(result)
t.same(result, {"user":"test-user","pass":"test-pass","verify":"test-pass","passMatch":true})
t.equal(output, 'Username: (test-user) Password: (<default hidden>) Password again: (<default hidden>) ')
t.end()
})
})
function child () {
read({prompt: "Username: ", default: "test-user" }, function (er, user) {
read({prompt: "Password: ", default: "test-pass", silent: true }, function (er, pass) {
read({prompt: "Password again: ", default: "test-pass", silent: true }, function (er, pass2) {
console.error(JSON.stringify({user: user,
pass: pass,
verify: pass2,
passMatch: (pass === pass2)}))
if (process.stdin.unref)
process.stdin.unref()
})
})
})
}

View File

@ -0,0 +1,83 @@
var tap = require('tap')
var read = require('../lib/read.js')
var CLOSE = 'close'
if (process.version.match(/^v0\.6/)) {
CLOSE = 'exit'
}
if (process.argv[2] === 'child') {
return child()
}
var spawn = require('child_process').spawn
function child () {
read({prompt:'1'}, function (er, r1) {if (er) throw er
read({prompt:'2'}, function (er, r2) {if (er) throw er
read({prompt:'3'}, function (er, r3) {if (er) throw er
read({prompt:'4'}, function (er, r4) {if (er) throw er
read({prompt:'5'}, function (er, r5) {if (er) throw er
read({prompt:'6'}, function (er, r6) {if (er) throw er
read({prompt:'7'}, function (er, r7) {if (er) throw er
read({prompt:'8'}, function (er, r8) {if (er) throw er
read({prompt:'9'}, function (er, r9) {if (er) throw er
read({prompt:'10'}, function (er, r10) {if (er) throw er
read({prompt:'11'}, function (er, r11) {if (er) throw er
read({prompt:'12'}, function (er, r12) {if (er) throw er
read({prompt:'13'}, function (er, r13) {if (er) throw er
read({prompt:'14'}, function (er, r14) {if (er) throw er
read({prompt:'15'}, function (er, r15) {if (er) throw er
read({prompt:'16'}, function (er, r16) {if (er) throw er
read({prompt:'17'}, function (er, r17) {if (er) throw er
read({prompt:'18'}, function (er, r18) {if (er) throw er
console.log(r1, r2, r3, r4, r5, r6, r7, r8, r9, r10,
r11, r12, r13, r14, r15, r16, r17, r18)
if (process.stdin.unref)
process.stdin.unref()
})})})})})})})})})})})})})})})})})})
}
tap.test('many reads', function (t) {
var child = spawn(process.execPath, [__filename, 'child'])
var n = 0
var output = ''
var expect = '1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ' +
'16 17 18 1 2 3 4 5 6 7 8 9 10 11 12 ' +
'13 14 15 16 17 18\n'
var write = child.stdin.write.bind(child.stdin)
var answers =
[ '1\n',
'2\n',
'3\n',
'4\n',
'5\n',
'6\n',
'7\n',
'8\n',
'9\n',
'10\n',
'11\n',
'12\n',
'13\n',
'14\n',
'15\n',
'16\n',
'17\n',
'18\n' ]
child.stdout.on('data', function (c) {
n++;
output += c
if (answers.length) {
write(answers.shift())
}
})
child.stderr.on('data', function (c) {
output += c
console.error('' + c)
})
child.on(CLOSE, function (c) {
t.equal(output, expect)
t.equal(n, 19)
t.end()
})
})

View File

@ -0,0 +1,2 @@
node_modules
npm-debug.log

View File

@ -0,0 +1,11 @@
language: node_js
node_js:
- "0.8"
- "0.10"
- "0.11"
notifications:
email:
- travis@nodejitsu.com
irc: "irc.freenode.org#nodejitsu"

View File

@ -0,0 +1,25 @@
0.1.3 / 2012-10-17
==================
* Fixed case problem with types
0.1.2 / 2012-06-27
==================
* Added host-name String format
* Added support for additionalProperties
* Added few default validation messages for formats
0.1.1 / 2012-04-16
==================
* Added default and custom error message support
* Added suport for conform function
* Updated date-time format
0.1.0 / 2011-11-09
=================
* Initial release

View File

@ -0,0 +1,179 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright (c) 2009-2010 Alexis Sellier, Charlie Robbins, Nodejitsu Inc.

View File

@ -0,0 +1,301 @@
# revalidator [![Build Status](https://secure.travis-ci.org/flatiron/revalidator.png)](http://travis-ci.org/flatiron/revalidator)
A cross-browser / node.js validator used by resourceful and flatiron. Revalidator has [JSONSchema](http://tools.ietf.org/html/draft-zyp-json-schema-04) compatibility as primary goal.
## Example
The core of `revalidator` is simple and succinct: `revalidator.validate(obj, schema)`:
``` js
var revalidator = require('revalidator');
console.dir(revalidator.validate(someObject, {
properties: {
url: {
description: 'the url the object should be stored at',
type: 'string',
pattern: '^/[^#%&*{}\\:<>?\/+]+$',
required: true
},
challenge: {
description: 'a means of protecting data (insufficient for production, used as example)',
type: 'string',
minLength: 5
},
body: {
description: 'what to store at the url',
type: 'any',
default: null
}
}
}));
```
This will return with a value indicating if the `obj` conforms to the `schema`. If it does not, a descriptive object will be returned containing the errors encountered with validation.
``` js
{
valid: true // or false
errors: [/* Array of errors if valid is false */]
}
```
In the browser, the validation function is exposed on `window.validate` by simply including `revalidator.js`.
## Installation
### Installing npm (node package manager)
``` bash
$ curl http://npmjs.org/install.sh | sh
```
### Installing revalidator
``` bash
$ [sudo] npm install revalidator
```
## Usage
`revalidator` takes json-schema as input to validate objects.
### revalidator.validate (obj, schema, options)
This will return with a value indicating if the `obj` conforms to the `schema`. If it does not, a descriptive object will be returned containing the errors encountered with validation.
``` js
{
valid: true // or false
errors: [/* Array of errors if valid is false */]
}
```
#### Available Options
* __validateFormats__: Enforce format constraints (_default true_)
* __validateFormatsStrict__: When `validateFormats` is _true_ treat unrecognized formats as validation errors (_default false_)
* __validateFormatExtensions__: When `validateFormats` is _true_ also validate formats defined in `validate.formatExtensions` (_default true_)
* __cast__: Enforce casting of some types (for integers/numbers are only supported) when it's possible, e.g. `"42" => 42`, but `"forty2" => "forty2"` for the `integer` type.
### Schema
For a property an `value` is that which is given as input for validation where as an `expected value` is the value of the below fields
#### required
If true, the value should not be undefined
```js
{ required: true }
```
#### allowEmpty
If false, the value must not be an empty string
```js
{ allowEmpty: false }
```
#### type
The `type of value` should be equal to the expected value
```js
{ type: 'string' }
{ type: 'number' }
{ type: 'integer' }
{ type: 'array' }
{ type: 'boolean' }
{ type: 'object' }
{ type: 'null' }
{ type: 'any' }
{ type: ['boolean', 'string'] }
```
#### pattern
The expected value regex needs to be satisfied by the value
```js
{ pattern: /^[a-z]+$/ }
```
#### maxLength
The length of value must be greater than or equal to expected value
```js
{ maxLength: 8 }
```
#### minLength
The length of value must be lesser than or equal to expected value
```js
{ minLength: 8 }
```
#### minimum
Value must be greater than or equal to the expected value
```js
{ minimum: 10 }
```
#### maximum
Value must be lesser than or equal to the expected value
```js
{ maximum: 10 }
```
#### allowEmpty
Value may not be empty
```js
{ allowEmpty: false }
```
#### exclusiveMinimum
Value must be greater than expected value
```js
{ exclusiveMinimum: 9 }
```
### exclusiveMaximum
Value must be lesser than expected value
```js
{ exclusiveMaximum: 11 }
```
#### divisibleBy
Value must be divisible by expected value
```js
{ divisibleBy: 5 }
{ divisibleBy: 0.5 }
```
#### minItems
Value must contain more then expected value number of items
```js
{ minItems: 2 }
```
#### maxItems
Value must contains less then expected value number of items
```js
{ maxItems: 5 }
```
#### uniqueItems
Value must hold a unique set of values
```js
{ uniqueItems: true }
```
#### enum
Value must be present in the array of expected value
```js
{ enum: ['month', 'year'] }
```
#### format
Value must be a valid format
```js
{ format: 'url' }
{ format: 'email' }
{ format: 'ip-address' }
{ format: 'ipv6' }
{ format: 'date-time' }
{ format: 'date' }
{ format: 'time' }
{ format: 'color' }
{ format: 'host-name' }
{ format: 'utc-millisec' }
{ format: 'regex' }
```
#### conform
Value must conform to constraint denoted by expected value
```js
{ conform: function (v) {
if (v%3==1) return true;
return false;
}
}
```
#### dependencies
Value is valid only if the dependent value is valid
```js
{
town: { required: true, dependencies: 'country' },
country: { maxLength: 3, required: true }
}
```
### Nested Schema
We also allow nested schema
```js
{
properties: {
title: {
type: 'string',
maxLength: 140,
required: true
},
author: {
type: 'object',
required: true,
properties: {
name: {
type: 'string',
required: true
},
email: {
type: 'string',
format: 'email'
}
}
}
}
}
```
### Custom Messages
We also allow custom message for different constraints
```js
{
type: 'string',
format: 'url'
messages: {
type: 'Not a string type',
format: 'Expected format is a url'
}
```
```js
{
conform: function () { ... },
message: 'This can be used as a global message'
}
```
## Tests
All tests are written with [vows][0] and should be run with [npm][1]:
``` bash
$ npm test
```
#### Author: [Charlie Robbins](http://nodejitsu.com), [Alexis Sellier](http://cloudhead.io)
#### Contributors: [Fedor Indutny](http://github.com/indutny), [Bradley Meck](http://github.com/bmeck), [Laurie Harper](http://laurie.holoweb.net/)
#### License: Apache 2.0
[0]: http://vowsjs.org
[1]: http://npmjs.org

View File

@ -0,0 +1,204 @@
//
// (C) 2011, Nodejitsu Inc.
// MIT License
//
// A simple web service for storing JSON data via REST
//
// GET - View Object
// POST - Create Object
// PUT - Update Object
// DELETE - Delete Object
//
var revalidator = require('../'),
http = require('http'),
//
// Keep our objects in a simple memory store
//
memoryStore = {},
//
// Set up our request schema
//
schema = {
properties: {
url: {
description: 'the url the object should be stored at',
type: 'string',
pattern: '^/[^#%&*{}\\:<>?\/+]+$',
required: true
},
challenge: {
description: 'a means of protecting data (insufficient for production, used as example)',
type: 'string',
minLength: 5
},
body: {
description: 'what to store at the url',
type: 'any',
default: null
}
}
}
var server = http.createServer(function validateRestRequest (req, res) {
req.method = req.method.toUpperCase();
//
// Log the requests
//
console.log(req.method, req.url);
//
// Buffer the request so it can be parsed as JSON
//
var requestBody = [];
req.on('data', function addDataToBody (data) {
requestBody.push(data);
});
//
// Once the request has ended work with the body
//
req.on('end', function dealWithRest () {
//
// Parse the JSON
//
requestBody = requestBody.join('');
if ({POST: 1, PUT: 1}[req.method]) {
try {
requestBody = JSON.parse(requestBody);
}
catch (e) {
res.writeHead(400);
res.end(e);
return;
}
}
else {
requestBody = {};
}
//
// If this was sent to a url but the body url was not declared
// Make sure the body get the requested url so that our schema
// validates before we work on it
//
if (!requestBody.url) {
requestBody.url = req.url;
}
//
// Don't let users override the main API endpoint
//
if (requestBody.url === '/') {
res.writeHead(400);
res.end('Cannot override the API endpoint "/"');
return;
}
//
// See if our request and target are out of sync
// This lets us double check the url we are about to take up
// if we choose to send the request to the url directly
//
if (req.url !== '/' && requestBody.url !== req.url) {
res.writeHead(400);
res.end('Requested url and actual url do not match');
return;
}
//
// Validate the schema
//
var validation = revalidator.validate(requestBody, schema);
if (!validation.valid) {
res.writeHead(400);
res.end(validation.errors.join('\n'));
return;
}
//
// Grab the current value from storage and
// check if it is a valid state for REST
//
var storedValue = memoryStore[requestBody.url];
if (req.method === 'POST') {
if (storedValue) {
res.writeHead(400);
res.end('ALREADY EXISTS');
return;
}
}
else if (!storedValue) {
res.writeHead(404);
res.end('DOES NOT EXIST');
return;
}
//
// Check our challenge
//
if (storedValue && requestBody.challenge != storedValue.challenge) {
res.writeHead(403);
res.end('NOT AUTHORIZED');
return;
}
//
// Since revalidator only checks and does not manipulate
// our object we need to set up the defaults our selves
// For an easier solution to this please look at Flatiron's
// `Resourceful` project
//
if (requestBody.body === undefined) {
requestBody.body = schema.properties.body.default;
}
//
// Use REST to determine how to manipulate the stored
// values
//
switch (req.method) {
case "GET":
res.writeHead(200);
var result = storedValue.body;
res.end(JSON.stringify(result));
return;
case "POST":
res.writeHead(201);
res.end();
memoryStore[requestBody.url] = requestBody;
return;
case "DELETE":
delete memoryStore[requestBody.url];
res.writeHead(200);
res.end();
return;
case "PUT":
memoryStore[requestBody.url] = requestBody;
res.writeHead(200);
res.end();
return;
default:
res.writeHead(400);
res.end('Invalid Http Verb');
return;
}
});
})
//
// Listen to various ports depending on environment we are being run on
//
server.listen(process.env.PORT || process.env.C9_PORT || 1337, function reportListening () {
console.log('JSON REST Service listening on port', this.address().port);
console.log('Requests can be sent via REST to "/" if they conform to the following schema:');
console.log(JSON.stringify(schema, null, ' '));
});

View File

@ -0,0 +1,427 @@
(function (exports) {
exports.validate = validate;
exports.mixin = mixin;
//
// ### function validate (object, schema, options)
// #### {Object} object the object to validate.
// #### {Object} schema (optional) the JSON Schema to validate against.
// #### {Object} options (optional) options controlling the validation
// process. See {@link #validate.defaults) for details.
// Validate <code>object</code> against a JSON Schema.
// If <code>object</code> is self-describing (i.e. has a
// <code>$schema</code> property), it will also be validated
// against the referenced schema. [TODO]: This behaviour bay be
// suppressed by setting the {@link #validate.options.???}
// option to <code>???</code>.[/TODO]
//
// If <code>schema</code> is not specified, and <code>object</code>
// is not self-describing, validation always passes.
//
// <strong>Note:</strong> in order to pass options but no schema,
// <code>schema</code> <em>must</em> be specified in the call to
// <code>validate()</code>; otherwise, <code>options</code> will
// be interpreted as the schema. <code>schema</code> may be passed
// as <code>null</code>, <code>undefinded</code>, or the empty object
// (<code>{}</code>) in this case.
//
function validate(object, schema, options) {
options = mixin({}, options, validate.defaults);
var errors = [];
validateObject(object, schema, options, errors);
//
// TODO: self-described validation
// if (! options.selfDescribing) { ... }
//
return {
valid: !(errors.length),
errors: errors
};
};
/**
* Default validation options. Defaults can be overridden by
* passing an 'options' hash to {@link #validate}. They can
* also be set globally be changing the values in
* <code>validate.defaults</code> directly.
*/
validate.defaults = {
/**
* <p>
* Enforce 'format' constraints.
* </p><p>
* <em>Default: <code>true</code></em>
* </p>
*/
validateFormats: true,
/**
* <p>
* When {@link #validateFormats} is <code>true</code>,
* treat unrecognized formats as validation errors.
* </p><p>
* <em>Default: <code>false</code></em>
* </p>
*
* @see validation.formats for default supported formats.
*/
validateFormatsStrict: false,
/**
* <p>
* When {@link #validateFormats} is <code>true</code>,
* also validate formats defined in {@link #validate.formatExtensions}.
* </p><p>
* <em>Default: <code>true</code></em>
* </p>
*/
validateFormatExtensions: true
};
/**
* Default messages to include with validation errors.
*/
validate.messages = {
required: "is required",
allowEmpty: "must not be empty",
minLength: "is too short (minimum is %{expected} characters)",
maxLength: "is too long (maximum is %{expected} characters)",
pattern: "invalid input",
minimum: "must be greater than or equal to %{expected}",
maximum: "must be less than or equal to %{expected}",
exclusiveMinimum: "must be greater than %{expected}",
exclusiveMaximum: "must be less than %{expected}",
divisibleBy: "must be divisible by %{expected}",
minItems: "must contain more than %{expected} items",
maxItems: "must contain less than %{expected} items",
uniqueItems: "must hold a unique set of values",
format: "is not a valid %{expected}",
conform: "must conform to given constraint",
type: "must be of %{expected} type"
};
validate.messages['enum'] = "must be present in given enumerator";
/**
*
*/
validate.formats = {
'email': /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i,
'ip-address': /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/i,
'ipv6': /^([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}$/,
'date-time': /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:.\d{1,3})?Z$/,
'date': /^\d{4}-\d{2}-\d{2}$/,
'time': /^\d{2}:\d{2}:\d{2}$/,
'color': /^#[a-z0-9]{6}|#[a-z0-9]{3}|(?:rgb\(\s*(?:[+-]?\d+%?)\s*,\s*(?:[+-]?\d+%?)\s*,\s*(?:[+-]?\d+%?)\s*\))aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow$/i,
//'style': (not supported)
//'phone': (not supported)
//'uri': (not supported)
'host-name': /^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])/,
'utc-millisec': {
test: function (value) {
return typeof(value) === 'number' && value >= 0;
}
},
'regex': {
test: function (value) {
try { new RegExp(value) }
catch (e) { return false }
return true;
}
}
};
/**
*
*/
validate.formatExtensions = {
'url': /^(https?|ftp|git):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i
};
function mixin(obj) {
var sources = Array.prototype.slice.call(arguments, 1);
while (sources.length) {
var source = sources.shift();
if (!source) { continue }
if (typeof(source) !== 'object') {
throw new TypeError('mixin non-object');
}
for (var p in source) {
if (source.hasOwnProperty(p)) {
obj[p] = source[p];
}
}
}
return obj;
};
function validateObject(object, schema, options, errors) {
var props, allProps = Object.keys(object),
visitedProps = [];
// see 5.2
if (schema.properties) {
props = schema.properties;
for (var p in props) {
if (props.hasOwnProperty(p)) {
visitedProps.push(p);
validateProperty(object, object[p], p, props[p], options, errors);
}
}
}
// see 5.3
if (schema.patternProperties) {
props = schema.patternProperties;
for (var p in props) {
if (props.hasOwnProperty(p)) {
var re = new RegExp(p);
// Find all object properties that are matching `re`
for (var k in object) {
if (object.hasOwnProperty(k)) {
visitedProps.push(k);
if (re.exec(k) !== null) {
validateProperty(object, object[k], p, props[p], options, errors);
}
}
}
}
}
}
// see 5.4
if (undefined !== schema.additionalProperties) {
var i, l;
var unvisitedProps = allProps.filter(function(k){
return -1 === visitedProps.indexOf(k);
});
// Prevent additional properties; each unvisited property is therefore an error
if (schema.additionalProperties === false && unvisitedProps.length > 0) {
for (i = 0, l = unvisitedProps.length; i < l; i++) {
error("additionalProperties", unvisitedProps[i], object[unvisitedProps[i]], false, errors);
}
}
// additionalProperties is a schema and validate unvisited properties against that schema
else if (typeof schema.additionalProperties == "object" && unvisitedProps.length > 0) {
for (i = 0, l = unvisitedProps.length; i < l; i++) {
validateProperty(object, object[unvisitedProps[i]], unvisitedProps[i], schema.unvisitedProperties, options, errors);
}
}
}
};
function validateProperty(object, value, property, schema, options, errors) {
var format,
valid,
spec,
type;
function constrain(name, value, assert) {
if (schema[name] !== undefined && !assert(value, schema[name])) {
error(name, property, value, schema, errors);
}
}
if (value === undefined) {
if (schema.required && schema.type !== 'any') {
return error('required', property, undefined, schema, errors);
} else {
return;
}
}
if (options.cast) {
if (('integer' === schema.type || 'number' === schema.type) && value == +value) {
value = +value;
object[property] = value;
}
if ('boolean' === schema.type) {
if ('true' === value || '1' === value || 1 === value) {
value = true;
object[property] = value;
}
if ('false' === value || '0' === value || 0 === value) {
value = false;
object[property] = value;
}
}
}
if (schema.format && options.validateFormats) {
format = schema.format;
if (options.validateFormatExtensions) { spec = validate.formatExtensions[format] }
if (!spec) { spec = validate.formats[format] }
if (!spec) {
if (options.validateFormatsStrict) {
return error('format', property, value, schema, errors);
}
}
else {
if (!spec.test(value)) {
return error('format', property, value, schema, errors);
}
}
}
if (schema['enum'] && schema['enum'].indexOf(value) === -1) {
error('enum', property, value, schema, errors);
}
// Dependencies (see 5.8)
if (typeof schema.dependencies === 'string' &&
object[schema.dependencies] === undefined) {
error('dependencies', property, null, schema, errors);
}
if (isArray(schema.dependencies)) {
for (var i = 0, l = schema.dependencies.length; i < l; i++) {
if (object[schema.dependencies[i]] === undefined) {
error('dependencies', property, null, schema, errors);
}
}
}
if (typeof schema.dependencies === 'object') {
validateObject(object, schema.dependencies, options, errors);
}
checkType(value, schema.type, function(err, type) {
if (err) return error('type', property, typeof value, schema, errors);
constrain('conform', value, function (a, e) { return e(a, object) });
switch (type || (isArray(value) ? 'array' : typeof value)) {
case 'string':
constrain('allowEmpty', value, function (a, e) { return e ? e : a !== '' });
constrain('minLength', value.length, function (a, e) { return a >= e });
constrain('maxLength', value.length, function (a, e) { return a <= e });
constrain('pattern', value, function (a, e) {
e = typeof e === 'string'
? e = new RegExp(e)
: e;
return e.test(a)
});
break;
case 'integer':
case 'number':
constrain('minimum', value, function (a, e) { return a >= e });
constrain('maximum', value, function (a, e) { return a <= e });
constrain('exclusiveMinimum', value, function (a, e) { return a > e });
constrain('exclusiveMaximum', value, function (a, e) { return a < e });
constrain('divisibleBy', value, function (a, e) {
var multiplier = Math.max((a - Math.floor(a)).toString().length - 2, (e - Math.floor(e)).toString().length - 2);
multiplier = multiplier > 0 ? Math.pow(10, multiplier) : 1;
return (a * multiplier) % (e * multiplier) === 0
});
break;
case 'array':
constrain('items', value, function (a, e) {
for (var i = 0, l = a.length; i < l; i++) {
validateProperty(object, a[i], property, e, options, errors);
}
return true;
});
constrain('minItems', value, function (a, e) { return a.length >= e });
constrain('maxItems', value, function (a, e) { return a.length <= e });
constrain('uniqueItems', value, function (a) {
var h = {};
for (var i = 0, l = a.length; i < l; i++) {
var key = JSON.stringify(a[i]);
if (h[key]) return false;
h[key] = true;
}
return true;
});
break;
case 'object':
// Recursive validation
if (schema.properties || schema.patternProperties || schema.additionalProperties) {
validateObject(value, schema, options, errors);
}
break;
}
});
};
function checkType(val, type, callback) {
var result = false,
types = isArray(type) ? type : [type];
// No type - no check
if (type === undefined) return callback(null, type);
// Go through available types
// And fine first matching
for (var i = 0, l = types.length; i < l; i++) {
type = types[i].toLowerCase().trim();
if (type === 'string' ? typeof val === 'string' :
type === 'array' ? isArray(val) :
type === 'object' ? val && typeof val === 'object' &&
!isArray(val) :
type === 'number' ? typeof val === 'number' :
type === 'integer' ? typeof val === 'number' && ~~val === val :
type === 'null' ? val === null :
type === 'boolean'? typeof val === 'boolean' :
type === 'date' ? isDate(val) :
type === 'any' ? typeof val !== 'undefined' : false) {
return callback(null, type);
}
};
callback(true);
};
function error(attribute, property, actual, schema, errors) {
var lookup = { expected: schema[attribute], actual: actual, attribute: attribute, property: property };
var message = schema.messages && schema.messages[attribute] || schema.message || validate.messages[attribute] || "no default message";
message = message.replace(/%\{([a-z]+)\}/ig, function (_, match) { return lookup[match.toLowerCase()] || ''; });
errors.push({
attribute: attribute,
property: property,
expected: schema[attribute],
actual: actual,
message: message
});
};
function isArray(value) {
var s = typeof value;
if (s === 'object') {
if (value) {
if (typeof value.length === 'number' &&
!(value.propertyIsEnumerable('length')) &&
typeof value.splice === 'function') {
return true;
}
}
}
return false;
}
function isDate(value) {
var s = typeof value;
if (s === 'object') {
if (value) {
if (typeof value.getTime === 'function') {
return true;
}
}
}
return false;
}
})(typeof module === 'object' && module && module.exports ? module.exports : window);

View File

@ -0,0 +1,57 @@
{
"name": "revalidator",
"version": "0.1.8",
"description": "A cross-browser / node.js validator used by resourceful",
"license": "Apache 2.0",
"author": {
"name": "Nodejitsu Inc.",
"email": "info@nodejitsu.com"
},
"maintainers": [
{
"name": "indexzero",
"email": "charlie.robbins@gmail.com"
},
{
"name": "fedor.indutny",
"email": "fedor.indutny@gmail.com"
},
{
"name": "julianduque",
"email": "julianduquej@gmail.com"
}
],
"repository": {
"type": "git",
"url": "http://github.com/flatiron/revalidator.git"
},
"devDependencies": {
"vows": "0.7.0"
},
"main": "./lib/revalidator",
"scripts": {
"test": "vows test/*-test.js --spec"
},
"engines": {
"node": ">= 0.4.0"
},
"bugs": {
"url": "https://github.com/flatiron/revalidator/issues"
},
"homepage": "https://github.com/flatiron/revalidator",
"_id": "revalidator@0.1.8",
"_shasum": "fece61bfa0c1b52a206bd6b18198184bdd523a3b",
"_from": "revalidator@0.1.x",
"_npmVersion": "1.4.10",
"_npmUser": {
"name": "julianduque",
"email": "julianduquej@gmail.com"
},
"dist": {
"shasum": "fece61bfa0c1b52a206bd6b18198184bdd523a3b",
"tarball": "http://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz"
},
"directories": {},
"_resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz",
"readme": "ERROR: No README data found!"
}

View File

@ -0,0 +1,421 @@
var assert = require('assert'),
vows = require('vows'),
revalidator = require('../lib/revalidator');
function clone(object) {
return Object.keys(object).reduce(function (obj, k) {
obj[k] = object[k];
return obj;
}, {});
};
function assertInvalid(res) {
assert.isObject(res);
assert.strictEqual(res.valid, false);
}
function assertValid(res) {
assert.isObject(res);
assert.strictEqual(res.valid, true);
}
function assertHasError(attr, field) {
return function (res) {
assert.notEqual(res.errors.length, 0);
assert.ok(res.errors.some(function (e) {
return e.attribute === attr && (field ? e.property === field : true);
}));
};
}
function assertHasErrorMsg(attr, msg) {
return function (res) {
assert.notEqual(res.errors.length, 0);
assert.ok(res.errors.some(function (e) {
return e.attribute === attr && e.message === msg;
}));
};
}
function assertValidates(passingValue, failingValue, attributes) {
var schema = {
name: 'Resource',
properties: { field: {} }
};
var failing;
if (!attributes) {
attributes = failingValue;
failing = false;
} else {
failing = true;
}
var attr = Object.keys(attributes)[0];
revalidator.mixin(schema.properties.field, attributes);
var result = {
"when the object conforms": {
topic: function () {
return revalidator.validate({ field: passingValue }, schema);
},
"return an object with `valid` set to true": assertValid
}
};
if (failing) {
result["when the object does not conform"] ={
topic: function () {
return revalidator.validate({ field: failingValue }, schema);
},
"return an object with `valid` set to false": assertInvalid,
"and an error concerning the attribute": assertHasError(Object.keys(attributes)[0], 'field')
};
};
return result;
}
vows.describe('revalidator', {
"Validating": {
"with <type>:'string'": assertValidates ('hello', 42, { type: "string" }),
"with <type>:'number'": assertValidates (42, 'hello', { type: "number" }),
"with <type>:'integer'": assertValidates (42, 42.5, { type: "integer" }),
"with <type>:'array'": assertValidates ([4, 2], 'hi', { type: "array" }),
"with <type>:'object'": assertValidates ({}, [], { type: "object" }),
"with <type>:'boolean'": assertValidates (false, 42, { type: "boolean" }),
"with <types>:bool,num": assertValidates (false, 'hello', { type: ["boolean", "number"] }),
"with <types>:bool,num": assertValidates (544, null, { type: ["boolean", "number"] }),
"with <type>:'null'": assertValidates (null, false, { type: "null" }),
"with <type>:'any'": assertValidates (9, { type: "any" }),
"with <type>:'date'": assertValidates (new Date(), 'hello', { type: "date" }),
"with <pattern>": assertValidates ("kaboom", "42", { pattern: /^[a-z]+$/ }),
"with <maxLength>": assertValidates ("boom", "kaboom", { maxLength: 4 }),
"with <minLength>": assertValidates ("kaboom", "boom", { minLength: 6 }),
"with <allowEmpty>": assertValidates ("hello", "", { allowEmpty: false }),
"with <minimum>": assertValidates ( 512, 43, { minimum: 473 }),
"with <maximum>": assertValidates ( 512, 1949, { maximum: 678 }),
"with <divisibleBy>": assertValidates ( 10, 9, { divisibleBy: 5 }),
"with <divisibleBy> decimal": assertValidates ( 0.2, 0.009, { divisibleBy: 0.01 }),
"with <enum>": assertValidates ("orange", "cigar", { enum: ["orange", "apple", "pear"] }),
"with <format>:'url'": assertValidates ('http://test.com/', 'hello', { format: 'url' }),
"with <dependencies>": {
topic: {
properties: {
town: { dependencies: "country" },
country: { }
}
},
"when the object conforms": {
topic: function (schema) {
return revalidator.validate({ town: "luna", country: "moon" }, schema);
},
"return an object with `valid` set to true": assertValid
},
"when the object does not conform": {
topic: function (schema) {
return revalidator.validate({ town: "luna" }, schema);
},
"return an object with `valid` set to false": assertInvalid,
"and an error concerning the attribute": assertHasError('dependencies')
}
},
"with <dependencies> as array": {
topic: {
properties: {
town: { dependencies: ["country", "planet"] },
country: { },
planet: { }
}
},
"when the object conforms": {
topic: function (schema) {
return revalidator.validate({ town: "luna", country: "moon", planet: "mars" }, schema);
},
"return an object with `valid` set to true": assertValid
},
"when the object does not conform": {
topic: function (schema) {
return revalidator.validate({ town: "luna", planet: "mars" }, schema);
},
"return an object with `valid` set to false": assertInvalid,
"and an error concerning the attribute": assertHasError('dependencies')
}
},
"with <dependencies> as schema": {
topic: {
properties: {
town: {
type: 'string',
dependencies: {
properties: { x: { type: "number" } }
}
},
country: { }
}
},
"when the object conforms": {
topic: function (schema) {
return revalidator.validate({ town: "luna", x: 1 }, schema);
},
"return an object with `valid` set to true": assertValid,
},
"when the object does not conform": {
topic: function (schema) {
return revalidator.validate({ town: "luna", x: 'no' }, schema);
},
"return an object with `valid` set to false": assertInvalid
}
},
"with <type>:'integer' and": {
"<minimum> constraints": assertValidates ( 512, 43, { minimum: 473, type: 'integer' }),
"<maximum> constraints": assertValidates ( 512, 1949, { maximum: 678, type: 'integer' }),
"<divisibleBy> constraints": assertValidates ( 10, 9, { divisibleBy: 5, type: 'integer' })
},
"with <additionalProperties>:false": {
topic: {
properties: {
town: { type: 'string' }
},
additionalProperties: false
},
"when the object conforms": {
topic: function (schema) {
return revalidator.validate({ town: "luna" }, schema);
},
"return an object with `valid` set to true": assertValid
},
"when the object does not conform": {
topic: function (schema) {
return revalidator.validate({ town: "luna", area: 'park' }, schema);
},
"return an object with `valid` set to false": assertInvalid
}
}
}
}).addBatch({
"A schema": {
topic: {
name: 'Article',
properties: {
title: {
type: 'string',
maxLength: 140,
conditions: {
optional: function () {
return !this.published;
}
}
},
date: { type: 'string', format: 'date', messages: { format: "must be a valid %{expected} and nothing else" } },
body: { type: 'string' },
tags: {
type: 'array',
uniqueItems: true,
minItems: 2,
items: {
type: 'string',
pattern: /[a-z ]+/
}
},
tuple: {
type: 'array',
minItems: 2,
maxItems: 2,
items: {
type: ['string', 'number']
}
},
author: { type: 'string', pattern: /^[\w ]+$/i, required: true, messages: { required: "is essential for survival" } },
published: { type: 'boolean', 'default': false },
category: { type: 'string' },
palindrome: {type: 'string', conform: function(val) {
return val == val.split("").reverse().join(""); }
},
name: { type: 'string', default: '', conform: function(val, data) {
return (val === data.author); }
}
},
patternProperties: {
'^_': {
type: 'boolean', default: false
}
}
},
"and an object": {
topic: {
title: 'Gimme some Gurus',
date: '2012-02-04',
body: "And I will pwn your codex.",
tags: ['energy drinks', 'code'],
tuple: ['string0', 103],
author: 'cloudhead',
published: true,
category: 'misc',
palindrome: 'dennis sinned',
name: 'cloudhead',
_flag: true
},
"can be validated with `revalidator.validate`": {
"and if it conforms": {
topic: function (object, schema) {
return revalidator.validate(object, schema);
},
"return an object with the `valid` property set to true": assertValid,
"return an object with the `errors` property as an empty array": function (res) {
assert.isArray(res.errors);
assert.isEmpty(res.errors);
}
},
"and if it has a missing required property": {
topic: function (object, schema) {
object = clone(object);
delete object.author;
return revalidator.validate(object, schema);
},
"return an object with `valid` set to false": assertInvalid,
"and an error concerning the 'required' attribute": assertHasError('required'),
"and the error message defined": assertHasErrorMsg('required', "is essential for survival")
},
"and if it has a missing non-required property": {
topic: function (object, schema) {
object = clone(object);
delete object.category;
return revalidator.validate(object, schema);
},
"return an object with `valid` set to false": assertValid
},
"and if it has a incorrect pattern property": {
topic: function (object, schema) {
object = clone(object);
object._additionalFlag = 'text';
return revalidator.validate(object, schema);
},
"return an object with `valid` set to false": assertInvalid
},
"and if it has a incorrect unique array property": {
topic: function (object, schema) {
object = clone(object);
object.tags = ['a', 'a'];
return revalidator.validate(object, schema);
},
"return an object with `valid` set to false": assertInvalid
},
"and if it has a incorrect array property (wrong values)": {
topic: function (object, schema) {
object = clone(object);
object.tags = ['a', '____'];
return revalidator.validate(object, schema);
},
"return an object with `valid` set to false": assertInvalid
},
"and if it has a incorrect array property (< minItems)": {
topic: function (object, schema) {
object = clone(object);
object.tags = ['x'];
return revalidator.validate(object, schema);
},
"return an object with `valid` set to false": assertInvalid
},
"and if it has a incorrect format (date)": {
topic: function (object, schema) {
object = clone(object);
object.date = 'bad date';
return revalidator.validate(object, schema);
},
"return an object with `valid` set to false": assertInvalid,
"and the error message defined": assertHasErrorMsg('format', "must be a valid date and nothing else")
},
"and if it is not a palindrome (conform function)": {
topic: function (object, schema) {
object = clone(object);
object.palindrome = 'bad palindrome';
return revalidator.validate(object, schema);
},
"return an object with `valid` set to false": assertInvalid
},
"and if it didn't validate a pattern": {
topic: function (object, schema) {
object = clone(object);
object.author = 'email@address.com';
return revalidator.validate(object, schema);
},
"return an object with `valid` set to false": assertInvalid,
"and an error concerning the 'pattern' attribute": assertHasError('pattern')
},
}
},
"with <cast> option": {
topic: {
properties: {
answer: { type: "integer" },
is_ready: { type: "boolean" }
}
},
"and <integer> property": {
"is castable string": {
topic: function (schema) {
return revalidator.validate({ answer: "42" }, schema, { cast: true });
},
"return an object with `valid` set to true": assertValid
},
"is uncastable string": {
topic: function (schema) {
return revalidator.validate({ answer: "forty2" }, schema, { cast: true });
},
"return an object with `valid` set to false": assertInvalid
},
"is casted to integer": {
topic: function (schema) {
var object = { answer: "42" };
revalidator.validate(object, schema, { cast: true });
return object;
},
"return an object with `answer` set to 42": function(res) { assert.strictEqual(res.answer, 42) }
}
},
"and <boolean> property": {
"is castable 'true/false' string": {
topic: function (schema) {
return revalidator.validate({ is_ready: "true" }, schema, { cast: true });
},
"return an object with `valid` set to true": assertValid
},
"is castable '1/0' string": {
topic: function (schema) {
return revalidator.validate({ is_ready: "1" }, schema, { cast: true });
},
"return an object with `valid` set to true": assertValid
},
"is castable `1/0` integer": {
topic: function (schema) {
return revalidator.validate({ is_ready: 1 }, schema, { cast: true });
},
"return an object with `valid` set to true": assertValid
},
"is uncastable string": {
topic: function (schema) {
return revalidator.validate({ is_ready: "not yet" }, schema, { cast: true });
},
"return an object with `valid` set to false": assertInvalid
},
"is uncastable number": {
topic: function (schema) {
return revalidator.validate({ is_ready: 42 }, schema, { cast: true });
},
"return an object with `valid` set to false": assertInvalid
},
"is casted to boolean": {
topic: function (schema) {
var object = { is_ready: "true" };
revalidator.validate(object, schema, { cast: true });
return object;
},
"return an object with `is_ready` set to true": function(res) { assert.strictEqual(res.is_ready, true) }
}
}
}
}
}).export(module);

View File

@ -0,0 +1,4 @@
node_modules
npm-debug.log
*.swp
*.swo

View File

@ -0,0 +1,10 @@
language: node_js
node_js:
- 0.6
- 0.8
notifications:
email:
- travis@nodejitsu.com
irc: "irc.freenode.org#nodejitsu"

View File

@ -0,0 +1,16 @@
0.1.5 / 2012-09-18
==================
* Fixed problem with underscore values in camelToUnderscore
0.1.4 / 2012-07-26
==================
* Made use of inflect for camel to underscore conversion
0.1.3 / 2012-07-25
==================
* Added camel to underscore conversion and vice-versa

View File

@ -0,0 +1,19 @@
Copyright (c) 2010 Nodejitsu Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,87 @@
# utile [![Build Status](https://secure.travis-ci.org/flatiron/utile.png)](http://travis-ci.org/flatiron/utile)
A drop-in replacement for `util` with some additional advantageous functions
## Motivation
Javascript is definitely a "batteries not included language" when compared to languages like Ruby or Python. Node.js has a simple utility library which exposes some basic (but important) functionality:
```
$ node
> var util = require('util');
> util.
(...)
util.debug util.error util.exec util.inherits util.inspect
util.log util.p util.print util.pump util.puts
```
When one considers their own utility library, why ever bother requiring `util` again? That is the approach taken by this module. To compare:
```
$ node
> var utile = require('./lib')
> utile.
(...)
utile.async utile.capitalize utile.clone utile.cpr utile.createPath utile.debug
utile.each utile.error utile.exec utile.file utile.filter utile.find
utile.inherits utile.log utile.mixin utile.mkdirp utile.p utile.path
utile.print utile.pump utile.puts utile.randomString utile.requireDir uile.requireDirLazy
utile.rimraf
```
As you can see all of the original methods from `util` are there, but there are several new methods specific to `utile`. A note about implementation: _no node.js native modules are modified by utile, it simply copies those methods._
## Methods
The `utile` modules exposes some simple utility methods:
* `.each(obj, iterator)`: Iterate over the keys of an object.
* `.mixin(target [source0, source1, ...])`: Copies enumerable properties from `source0 ... sourceN` onto `target` and returns the resulting object.
* `.clone(obj)`: Shallow clones the specified object.
* `.capitalize(str)`: Capitalizes the specified `str`.
* `.randomString(length)`: randomString returns a pseudo-random ASCII string (subset) the return value is a string of length ⌈bits/6⌉ of characters from the base64 alphabet.
* `.filter(obj, test)`: return an object with the properties that `test` returns true on.
* `.args(arguments)`: Converts function arguments into actual array with special `callback`, `cb`, `array`, and `last` properties. Also supports *optional* argument contracts. See [the example](https://github.com/flatiron/utile/blob/master/examples/utile-args.js) for more details.
* `.requireDir(directory)`: Requires all files and directories from `directory`, returning an object with keys being filenames (without trailing `.js`) and respective values being return values of `require(filename)`.
* `.requireDirLazy(directory)`: Lazily requires all files and directories from `directory`, returning an object with keys being filenames (without trailing `.js`) and respective values (getters) being return values of `require(filename)`.
* `.format([string] text, [array] formats, [array] replacements)`: Replace `formats` in `text` with `replacements`. This will fall back to the original `util.format` command if it is called improperly.
## Packaged Dependencies
In addition to the methods that are built-in, utile includes a number of commonly used dependencies to reduce the number of includes in your package.json. These modules _are not eagerly loaded to be respectful of startup time,_ but instead are lazy-loaded getters on the `utile` object
* `.async`: [Async utilities for node and the browser][0]
* `.inflect`: [Customizable inflections for node.js][6]
* `.mkdirp`: [Recursively mkdir, like mkdir -p, but in node.js][1]
* `.rimraf`: [A rm -rf util for nodejs][2]
* `.cpr`: [Asynchronous recursive file copying with Node.js][3]
## Installation
### Installing npm (node package manager)
```
curl http://npmjs.org/install.sh | sh
```
### Installing utile
```
[sudo] npm install utile
```
## Tests
All tests are written with [vows][4] and should be run with [npm][5]:
``` bash
$ npm test
```
#### Author: [Nodejitsu Inc.](http://www.nodejitsu.com)
#### Contributors: [Charlie Robbins](http://github.com/indexzero), [Dominic Tarr](http://github.com/dominictarr)
#### License: MIT
[0]: https://github.com/caolan/async
[1]: https://github.com/substack/node-mkdirp
[2]: https://github.com/isaacs/rimraf
[3]: https://github.com/avianflu/ncp
[4]: https://vowsjs.org
[5]: https://npmjs.org
[6]: https://github.com/pksunkara/inflect

View File

@ -0,0 +1,46 @@
/*
* args.js: function argument parsing helper utility
*
* (C) 2012, Nodejitsu Inc.
* MIT LICENSE
*
*/
var utile = require('./index');
//
// ### function args(_args)
// #### _args {Arguments} Original function arguments
//
// Top-level method will accept a javascript "arguments" object (the actual keyword
// "arguments" inside any scope), and attempt to return back an intelligent object
// representing the functions arguments
//
module.exports = function (_args) {
var args = utile.rargs(_args),
_cb;
//
// Find and define the first argument
//
Object.defineProperty(args, 'first', { value: args[0] });
//
// Find and define any callback
//
_cb = args[args.length - 1] || args[args.length];
if (typeof _cb === "function") {
Object.defineProperty(args, 'callback', { value: _cb });
Object.defineProperty(args, 'cb', { value: _cb });
args.pop();
}
//
// Find and define the last argument
//
if (args.length) {
Object.defineProperty(args, 'last', { value: args[args.length - 1] });
}
return args;
};

View File

@ -0,0 +1,44 @@
/*
* base64.js: An extremely simple implementation of base64 encoding / decoding using node.js Buffers
*
* (C) 2010, Nodejitsu Inc.
*
*/
var base64 = exports;
//
// ### function encode (unencoded)
// #### @unencoded {string} The string to base64 encode
// Encodes the specified string to base64 using node.js Buffers.
//
base64.encode = function (unencoded) {
var encoded;
try {
encoded = new Buffer(unencoded || '').toString('base64');
}
catch (ex) {
return null;
}
return encoded;
};
//
// ### function decode (encoded)
// #### @encoded {string} The string to base64 decode
// Decodes the specified string from base64 using node.js Buffers.
//
base64.decode = function (encoded) {
var decoded;
try {
decoded = new Buffer(encoded || '', 'base64').toString('utf8');
}
catch (ex) {
return null;
}
return decoded;
};

View File

@ -0,0 +1,33 @@
/*
* file.js: Simple utilities for working with the file system.
*
* (C) 2011, Nodejitsu Inc.
* MIT LICENSE
*
*/
var fs = require('fs');
exports.readJson = exports.readJSON = function (file, callback) {
if (typeof callback !== 'function') {
throw new Error('utile.file.readJson needs a callback');
}
fs.readFile(file, 'utf-8', function (err, data) {
if (err) {
return callback(err);
}
try {
var json = JSON.parse(data);
callback(null, json);
}
catch (err) {
return callback(err);
}
});
};
exports.readJsonSync = exports.readJSONSync = function (file) {
return JSON.parse(fs.readFileSync(file, 'utf-8'));
};

View File

@ -0,0 +1,25 @@
/*
* format.js: `util.format` enhancement to allow custom formatting parameters.
*
* (C) 2012, Nodejitsu Inc.
* MIT LICENSE
*
*/
var util = require('util');
exports = module.exports = function(str) {
var formats = [].slice.call(arguments, 1, 3);
if (!(formats[0] instanceof Array && formats[1] instanceof Array) || arguments.length > 3)
return util.format.apply(null, arguments);
var replacements = formats.pop(),
formats = formats.shift();
formats.forEach(function(format, id) {
str = str.replace(new RegExp(format), replacements[id]);
});
return str;
};

View File

@ -0,0 +1,467 @@
/*
* index.js: Top-level include for the `utile` module.
*
* (C) 2011, Nodejitsu Inc.
* MIT LICENSE
*
*/
var fs = require('fs'),
path = require('path'),
util = require('util');
var utile = module.exports;
//
// Extend the `utile` object with all methods from the
// core node `util` methods.
//
Object.keys(util).forEach(function (key) {
utile[key] = util[key];
});
Object.defineProperties(utile, {
//
// ### function async
// Simple wrapper to `require('async')`.
//
'async': {
get: function() {
return utile.async = require('async');
}
},
//
// ### function inflect
// Simple wrapper to `require('i')`.
//
'inflect': {
get: function() {
return utile.inflect = require('i')();
}
},
//
// ### function mkdirp
// Simple wrapper to `require('mkdirp')`
//
'mkdirp': {
get: function() {
return utile.mkdirp = require('mkdirp');
}
},
//
// ### function deepEqual
// Simple wrapper to `require('deep-equal')`
// Remark: deepEqual is 4x faster then using assert.deepEqual
// see: https://gist.github.com/2790507
//
'deepEqual': {
get: function() {
return utile.deepEqual = require('deep-equal');
}
},
//
// ### function rimraf
// Simple wrapper to `require('rimraf')`
//
'rimraf': {
get: function() {
return utile.rimraf = require('rimraf');
}
},
//
// ### function cpr
// Simple wrapper to `require('ncp').ncp`
//
'cpr': {
get: function() {
return utile.cpr = require('ncp').ncp;
}
},
//
// ### @file {Object}
// Lazy-loaded `file` module
//
'file': {
get: function() {
return utile.file = require('./file');
}
},
//
// ### @args {Object}
// Lazy-loaded `args` module
//
'args': {
get: function() {
return utile.args = require('./args');
}
},
//
// ### @base64 {Object}
// Lazy-loaded `base64` object
//
'base64': {
get: function() {
return utile.base64 = require('./base64');
}
},
//
// ### @format {Object}
// Lazy-loaded `format` object
//
'format': {
get: function() {
return utile.format = require('./format');
}
}
});
//
// ### function rargs(_args)
// #### _args {Arguments} Original function arguments
//
// Top-level method will accept a javascript "arguments" object
// (the actual keyword "arguments" inside any scope) and return
// back an Array.
//
utile.rargs = function (_args, slice) {
if (!slice) {
slice = 0;
}
var len = (_args || []).length,
args = new Array(len - slice),
i;
//
// Convert the raw `_args` to a proper Array.
//
for (i = slice; i < len; i++) {
args[i - slice] = _args[i];
}
return args;
};
//
// ### function each (obj, iterator)
// #### @obj {Object} Object to iterate over
// #### @iterator {function} Continuation to use on each key. `function (value, key, object)`
// Iterate over the keys of an object.
//
utile.each = function (obj, iterator) {
Object.keys(obj).forEach(function (key) {
iterator(obj[key], key, obj);
});
};
//
// ### function find (o)
//
//
utile.find = function (obj, pred) {
var value, key;
for (key in obj) {
value = obj[key];
if (pred(value, key)) {
return value;
}
}
};
//
// ### function pad (str, len, chr)
// ### @str {String} String to pad
// ### @len {Number} Number of chars to pad str with
// ### @chr {String} Optional replacement character, defaults to empty space
// Appends chr to str until it reaches a length of len
//
utile.pad = function pad(str, len, chr) {
var s;
if (!chr) {
chr = ' ';
}
str = str || '';
s = str;
if (str.length < len) {
for (var i = 0; i < (len - str.length); i++) {
s += chr;
}
}
return s;
}
//
// ### function path (obj, path, value)
// ### @obj {Object} Object to insert value into
// ### @path {Array} List of nested keys to insert value at
// Retreives a value from given Object, `obj`, located at the
// nested keys, `path`.
//
utile.path = function (obj, path) {
var key, i;
for (i in path) {
if (typeof obj === 'undefined') {
return undefined;
}
key = path[i];
obj = obj[key];
}
return obj;
};
//
// ### function createPath (obj, path, value)
// ### @obj {Object} Object to insert value into
// ### @path {Array} List of nested keys to insert value at
// ### @value {*} Value to insert into the object.
// Inserts the `value` into the given Object, `obj`, creating
// any keys in `path` along the way if necessary.
//
utile.createPath = function (obj, path, value) {
var key, i;
for (i in path) {
key = path[i];
if (!obj[key]) {
obj[key] = ((+i + 1 === path.length) ? value : {});
}
obj = obj[key];
}
};
//
// ### function mixin (target [source0, source1, ...])
// Copies enumerable properties from `source0 ... sourceN`
// onto `target` and returns the resulting object.
//
utile.mixin = function (target) {
utile.rargs(arguments, 1).forEach(function (o) {
Object.getOwnPropertyNames(o).forEach(function(attr) {
var getter = Object.getOwnPropertyDescriptor(o, attr).get,
setter = Object.getOwnPropertyDescriptor(o, attr).set;
if (!getter && !setter) {
target[attr] = o[attr];
}
else {
Object.defineProperty(target, attr, {
get: getter,
set: setter
});
}
});
});
return target;
};
//
// ### function capitalize (str)
// #### @str {string} String to capitalize
// Capitalizes the specified `str`.
//
utile.capitalize = utile.inflect.camelize;
//
// ### function escapeRegExp (str)
// #### @str {string} String to be escaped
// Escape string for use in Javascript regex
//
utile.escapeRegExp = function (str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
};
//
// ### function randomString (length)
// #### @length {integer} The number of bits for the random base64 string returned to contain
// randomString returns a pseude-random ASCII string (subset)
// the return value is a string of length ⌈bits/6⌉ of characters
// from the base64 alphabet.
//
utile.randomString = function (length) {
var chars, rand, i, ret, mod, bits;
chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-';
ret = '';
// standard 4
mod = 4;
// default is 16
bits = length * mod || 64;
// in v8, Math.random() yields 32 pseudo-random bits (in spidermonkey it gives 53)
while (bits > 0) {
// 32-bit integer
rand = Math.floor(Math.random() * 0x100000000);
//we use the top bits
for (i = 26; i > 0 && bits > 0; i -= mod, bits -= mod) {
ret += chars[0x3F & rand >>> i];
}
}
return ret;
};
//
// ### function filter (object, test)
// #### @obj {Object} Object to iterate over
// #### @pred {function} Predicate applied to each property. `function (value, key, object)`
// Returns an object with properties from `obj` which satisfy
// the predicate `pred`
//
utile.filter = function (obj, pred) {
var copy;
if (Array.isArray(obj)) {
copy = [];
utile.each(obj, function (val, key) {
if (pred(val, key, obj)) {
copy.push(val);
}
});
}
else {
copy = {};
utile.each(obj, function (val, key) {
if (pred(val, key, obj)) {
copy[key] = val;
}
});
}
return copy;
};
//
// ### function requireDir (directory)
// #### @directory {string} Directory to require
// Requires all files and directories from `directory`, returning an object
// with keys being filenames (without trailing `.js`) and respective values
// being return values of `require(filename)`.
//
utile.requireDir = function (directory) {
var result = {},
files = fs.readdirSync(directory);
files.forEach(function (file) {
if (file.substr(-3) === '.js') {
file = file.substr(0, file.length - 3);
}
result[file] = require(path.resolve(directory, file));
});
return result;
};
//
// ### function requireDirLazy (directory)
// #### @directory {string} Directory to require
// Lazily requires all files and directories from `directory`, returning an
// object with keys being filenames (without trailing `.js`) and respective
// values (getters) being return values of `require(filename)`.
//
utile.requireDirLazy = function (directory) {
var result = {},
files = fs.readdirSync(directory);
files.forEach(function (file) {
if (file.substr(-3) === '.js') {
file = file.substr(0, file.length - 3);
}
Object.defineProperty(result, file, {
get: function() {
return result[file] = require(path.resolve(directory, file));
}
});
});
return result;
};
//
// ### function clone (object, filter)
// #### @object {Object} Object to clone
// #### @filter {Function} Filter to be used
// Shallow clones the specified object.
//
utile.clone = function (object, filter) {
return Object.keys(object).reduce(filter ? function (obj, k) {
if (filter(k)) obj[k] = object[k];
return obj;
} : function (obj, k) {
obj[k] = object[k];
return obj;
}, {});
};
//
// ### function camelToUnderscore (obj)
// #### @obj {Object} Object to convert keys on.
// Converts all keys of the type `keyName` to `key_name` on the
// specified `obj`.
//
utile.camelToUnderscore = function (obj) {
if (typeof obj !== 'object' || obj === null) {
return obj;
}
if (Array.isArray(obj)) {
obj.forEach(utile.camelToUnderscore);
return obj;
}
Object.keys(obj).forEach(function (key) {
var k = utile.inflect.underscore(key);
if (k !== key) {
obj[k] = obj[key];
delete obj[key];
key = k;
}
utile.camelToUnderscore(obj[key]);
});
return obj;
};
//
// ### function underscoreToCamel (obj)
// #### @obj {Object} Object to convert keys on.
// Converts all keys of the type `key_name` to `keyName` on the
// specified `obj`.
//
utile.underscoreToCamel = function (obj) {
if (typeof obj !== 'object' || obj === null) {
return obj;
}
if (Array.isArray(obj)) {
obj.forEach(utile.underscoreToCamel);
return obj;
}
Object.keys(obj).forEach(function (key) {
var k = utile.inflect.camelize(key, false);
if (k !== key) {
obj[k] = obj[key];
delete obj[key];
key = k;
}
utile.underscoreToCamel(obj[key]);
});
return obj;
};

View File

@ -0,0 +1,19 @@
Copyright (c) 2010 Caolan McMahon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,11 @@
{
"name": "async",
"repo": "caolan/async",
"description": "Higher-order functions and common patterns for asynchronous code",
"version": "0.1.23",
"keywords": [],
"dependencies": {},
"development": {},
"main": "lib/async.js",
"scripts": [ "lib/async.js" ]
}

View File

@ -0,0 +1,958 @@
/*global setImmediate: false, setTimeout: false, console: false */
(function () {
var async = {};
// global on the server, window in the browser
var root, previous_async;
root = this;
if (root != null) {
previous_async = root.async;
}
async.noConflict = function () {
root.async = previous_async;
return async;
};
function only_once(fn) {
var called = false;
return function() {
if (called) throw new Error("Callback was already called.");
called = true;
fn.apply(root, arguments);
}
}
//// cross-browser compatiblity functions ////
var _each = function (arr, iterator) {
if (arr.forEach) {
return arr.forEach(iterator);
}
for (var i = 0; i < arr.length; i += 1) {
iterator(arr[i], i, arr);
}
};
var _map = function (arr, iterator) {
if (arr.map) {
return arr.map(iterator);
}
var results = [];
_each(arr, function (x, i, a) {
results.push(iterator(x, i, a));
});
return results;
};
var _reduce = function (arr, iterator, memo) {
if (arr.reduce) {
return arr.reduce(iterator, memo);
}
_each(arr, function (x, i, a) {
memo = iterator(memo, x, i, a);
});
return memo;
};
var _keys = function (obj) {
if (Object.keys) {
return Object.keys(obj);
}
var keys = [];
for (var k in obj) {
if (obj.hasOwnProperty(k)) {
keys.push(k);
}
}
return keys;
};
//// exported async module functions ////
//// nextTick implementation with browser-compatible fallback ////
if (typeof process === 'undefined' || !(process.nextTick)) {
if (typeof setImmediate === 'function') {
async.nextTick = function (fn) {
// not a direct alias for IE10 compatibility
setImmediate(fn);
};
async.setImmediate = async.nextTick;
}
else {
async.nextTick = function (fn) {
setTimeout(fn, 0);
};
async.setImmediate = async.nextTick;
}
}
else {
async.nextTick = process.nextTick;
if (typeof setImmediate !== 'undefined') {
async.setImmediate = function (fn) {
// not a direct alias for IE10 compatibility
setImmediate(fn);
};
}
else {
async.setImmediate = async.nextTick;
}
}
async.each = function (arr, iterator, callback) {
callback = callback || function () {};
if (!arr.length) {
return callback();
}
var completed = 0;
_each(arr, function (x) {
iterator(x, only_once(function (err) {
if (err) {
callback(err);
callback = function () {};
}
else {
completed += 1;
if (completed >= arr.length) {
callback(null);
}
}
}));
});
};
async.forEach = async.each;
async.eachSeries = function (arr, iterator, callback) {
callback = callback || function () {};
if (!arr.length) {
return callback();
}
var completed = 0;
var iterate = function () {
iterator(arr[completed], function (err) {
if (err) {
callback(err);
callback = function () {};
}
else {
completed += 1;
if (completed >= arr.length) {
callback(null);
}
else {
iterate();
}
}
});
};
iterate();
};
async.forEachSeries = async.eachSeries;
async.eachLimit = function (arr, limit, iterator, callback) {
var fn = _eachLimit(limit);
fn.apply(null, [arr, iterator, callback]);
};
async.forEachLimit = async.eachLimit;
var _eachLimit = function (limit) {
return function (arr, iterator, callback) {
callback = callback || function () {};
if (!arr.length || limit <= 0) {
return callback();
}
var completed = 0;
var started = 0;
var running = 0;
(function replenish () {
if (completed >= arr.length) {
return callback();
}
while (running < limit && started < arr.length) {
started += 1;
running += 1;
iterator(arr[started - 1], function (err) {
if (err) {
callback(err);
callback = function () {};
}
else {
completed += 1;
running -= 1;
if (completed >= arr.length) {
callback();
}
else {
replenish();
}
}
});
}
})();
};
};
var doParallel = function (fn) {
return function () {
var args = Array.prototype.slice.call(arguments);
return fn.apply(null, [async.each].concat(args));
};
};
var doParallelLimit = function(limit, fn) {
return function () {
var args = Array.prototype.slice.call(arguments);
return fn.apply(null, [_eachLimit(limit)].concat(args));
};
};
var doSeries = function (fn) {
return function () {
var args = Array.prototype.slice.call(arguments);
return fn.apply(null, [async.eachSeries].concat(args));
};
};
var _asyncMap = function (eachfn, arr, iterator, callback) {
var results = [];
arr = _map(arr, function (x, i) {
return {index: i, value: x};
});
eachfn(arr, function (x, callback) {
iterator(x.value, function (err, v) {
results[x.index] = v;
callback(err);
});
}, function (err) {
callback(err, results);
});
};
async.map = doParallel(_asyncMap);
async.mapSeries = doSeries(_asyncMap);
async.mapLimit = function (arr, limit, iterator, callback) {
return _mapLimit(limit)(arr, iterator, callback);
};
var _mapLimit = function(limit) {
return doParallelLimit(limit, _asyncMap);
};
// reduce only has a series version, as doing reduce in parallel won't
// work in many situations.
async.reduce = function (arr, memo, iterator, callback) {
async.eachSeries(arr, function (x, callback) {
iterator(memo, x, function (err, v) {
memo = v;
callback(err);
});
}, function (err) {
callback(err, memo);
});
};
// inject alias
async.inject = async.reduce;
// foldl alias
async.foldl = async.reduce;
async.reduceRight = function (arr, memo, iterator, callback) {
var reversed = _map(arr, function (x) {
return x;
}).reverse();
async.reduce(reversed, memo, iterator, callback);
};
// foldr alias
async.foldr = async.reduceRight;
var _filter = function (eachfn, arr, iterator, callback) {
var results = [];
arr = _map(arr, function (x, i) {
return {index: i, value: x};
});
eachfn(arr, function (x, callback) {
iterator(x.value, function (v) {
if (v) {
results.push(x);
}
callback();
});
}, function (err) {
callback(_map(results.sort(function (a, b) {
return a.index - b.index;
}), function (x) {
return x.value;
}));
});
};
async.filter = doParallel(_filter);
async.filterSeries = doSeries(_filter);
// select alias
async.select = async.filter;
async.selectSeries = async.filterSeries;
var _reject = function (eachfn, arr, iterator, callback) {
var results = [];
arr = _map(arr, function (x, i) {
return {index: i, value: x};
});
eachfn(arr, function (x, callback) {
iterator(x.value, function (v) {
if (!v) {
results.push(x);
}
callback();
});
}, function (err) {
callback(_map(results.sort(function (a, b) {
return a.index - b.index;
}), function (x) {
return x.value;
}));
});
};
async.reject = doParallel(_reject);
async.rejectSeries = doSeries(_reject);
var _detect = function (eachfn, arr, iterator, main_callback) {
eachfn(arr, function (x, callback) {
iterator(x, function (result) {
if (result) {
main_callback(x);
main_callback = function () {};
}
else {
callback();
}
});
}, function (err) {
main_callback();
});
};
async.detect = doParallel(_detect);
async.detectSeries = doSeries(_detect);
async.some = function (arr, iterator, main_callback) {
async.each(arr, function (x, callback) {
iterator(x, function (v) {
if (v) {
main_callback(true);
main_callback = function () {};
}
callback();
});
}, function (err) {
main_callback(false);
});
};
// any alias
async.any = async.some;
async.every = function (arr, iterator, main_callback) {
async.each(arr, function (x, callback) {
iterator(x, function (v) {
if (!v) {
main_callback(false);
main_callback = function () {};
}
callback();
});
}, function (err) {
main_callback(true);
});
};
// all alias
async.all = async.every;
async.sortBy = function (arr, iterator, callback) {
async.map(arr, function (x, callback) {
iterator(x, function (err, criteria) {
if (err) {
callback(err);
}
else {
callback(null, {value: x, criteria: criteria});
}
});
}, function (err, results) {
if (err) {
return callback(err);
}
else {
var fn = function (left, right) {
var a = left.criteria, b = right.criteria;
return a < b ? -1 : a > b ? 1 : 0;
};
callback(null, _map(results.sort(fn), function (x) {
return x.value;
}));
}
});
};
async.auto = function (tasks, callback) {
callback = callback || function () {};
var keys = _keys(tasks);
if (!keys.length) {
return callback(null);
}
var results = {};
var listeners = [];
var addListener = function (fn) {
listeners.unshift(fn);
};
var removeListener = function (fn) {
for (var i = 0; i < listeners.length; i += 1) {
if (listeners[i] === fn) {
listeners.splice(i, 1);
return;
}
}
};
var taskComplete = function () {
_each(listeners.slice(0), function (fn) {
fn();
});
};
addListener(function () {
if (_keys(results).length === keys.length) {
callback(null, results);
callback = function () {};
}
});
_each(keys, function (k) {
var task = (tasks[k] instanceof Function) ? [tasks[k]]: tasks[k];
var taskCallback = function (err) {
var args = Array.prototype.slice.call(arguments, 1);
if (args.length <= 1) {
args = args[0];
}
if (err) {
var safeResults = {};
_each(_keys(results), function(rkey) {
safeResults[rkey] = results[rkey];
});
safeResults[k] = args;
callback(err, safeResults);
// stop subsequent errors hitting callback multiple times
callback = function () {};
}
else {
results[k] = args;
async.setImmediate(taskComplete);
}
};
var requires = task.slice(0, Math.abs(task.length - 1)) || [];
var ready = function () {
return _reduce(requires, function (a, x) {
return (a && results.hasOwnProperty(x));
}, true) && !results.hasOwnProperty(k);
};
if (ready()) {
task[task.length - 1](taskCallback, results);
}
else {
var listener = function () {
if (ready()) {
removeListener(listener);
task[task.length - 1](taskCallback, results);
}
};
addListener(listener);
}
});
};
async.waterfall = function (tasks, callback) {
callback = callback || function () {};
if (tasks.constructor !== Array) {
var err = new Error('First argument to waterfall must be an array of functions');
return callback(err);
}
if (!tasks.length) {
return callback();
}
var wrapIterator = function (iterator) {
return function (err) {
if (err) {
callback.apply(null, arguments);
callback = function () {};
}
else {
var args = Array.prototype.slice.call(arguments, 1);
var next = iterator.next();
if (next) {
args.push(wrapIterator(next));
}
else {
args.push(callback);
}
async.setImmediate(function () {
iterator.apply(null, args);
});
}
};
};
wrapIterator(async.iterator(tasks))();
};
var _parallel = function(eachfn, tasks, callback) {
callback = callback || function () {};
if (tasks.constructor === Array) {
eachfn.map(tasks, function (fn, callback) {
if (fn) {
fn(function (err) {
var args = Array.prototype.slice.call(arguments, 1);
if (args.length <= 1) {
args = args[0];
}
callback.call(null, err, args);
});
}
}, callback);
}
else {
var results = {};
eachfn.each(_keys(tasks), function (k, callback) {
tasks[k](function (err) {
var args = Array.prototype.slice.call(arguments, 1);
if (args.length <= 1) {
args = args[0];
}
results[k] = args;
callback(err);
});
}, function (err) {
callback(err, results);
});
}
};
async.parallel = function (tasks, callback) {
_parallel({ map: async.map, each: async.each }, tasks, callback);
};
async.parallelLimit = function(tasks, limit, callback) {
_parallel({ map: _mapLimit(limit), each: _eachLimit(limit) }, tasks, callback);
};
async.series = function (tasks, callback) {
callback = callback || function () {};
if (tasks.constructor === Array) {
async.mapSeries(tasks, function (fn, callback) {
if (fn) {
fn(function (err) {
var args = Array.prototype.slice.call(arguments, 1);
if (args.length <= 1) {
args = args[0];
}
callback.call(null, err, args);
});
}
}, callback);
}
else {
var results = {};
async.eachSeries(_keys(tasks), function (k, callback) {
tasks[k](function (err) {
var args = Array.prototype.slice.call(arguments, 1);
if (args.length <= 1) {
args = args[0];
}
results[k] = args;
callback(err);
});
}, function (err) {
callback(err, results);
});
}
};
async.iterator = function (tasks) {
var makeCallback = function (index) {
var fn = function () {
if (tasks.length) {
tasks[index].apply(null, arguments);
}
return fn.next();
};
fn.next = function () {
return (index < tasks.length - 1) ? makeCallback(index + 1): null;
};
return fn;
};
return makeCallback(0);
};
async.apply = function (fn) {
var args = Array.prototype.slice.call(arguments, 1);
return function () {
return fn.apply(
null, args.concat(Array.prototype.slice.call(arguments))
);
};
};
var _concat = function (eachfn, arr, fn, callback) {
var r = [];
eachfn(arr, function (x, cb) {
fn(x, function (err, y) {
r = r.concat(y || []);
cb(err);
});
}, function (err) {
callback(err, r);
});
};
async.concat = doParallel(_concat);
async.concatSeries = doSeries(_concat);
async.whilst = function (test, iterator, callback) {
if (test()) {
iterator(function (err) {
if (err) {
return callback(err);
}
async.whilst(test, iterator, callback);
});
}
else {
callback();
}
};
async.doWhilst = function (iterator, test, callback) {
iterator(function (err) {
if (err) {
return callback(err);
}
if (test()) {
async.doWhilst(iterator, test, callback);
}
else {
callback();
}
});
};
async.until = function (test, iterator, callback) {
if (!test()) {
iterator(function (err) {
if (err) {
return callback(err);
}
async.until(test, iterator, callback);
});
}
else {
callback();
}
};
async.doUntil = function (iterator, test, callback) {
iterator(function (err) {
if (err) {
return callback(err);
}
if (!test()) {
async.doUntil(iterator, test, callback);
}
else {
callback();
}
});
};
async.queue = function (worker, concurrency) {
if (concurrency === undefined) {
concurrency = 1;
}
function _insert(q, data, pos, callback) {
if(data.constructor !== Array) {
data = [data];
}
_each(data, function(task) {
var item = {
data: task,
callback: typeof callback === 'function' ? callback : null
};
if (pos) {
q.tasks.unshift(item);
} else {
q.tasks.push(item);
}
if (q.saturated && q.tasks.length === concurrency) {
q.saturated();
}
async.setImmediate(q.process);
});
}
var workers = 0;
var q = {
tasks: [],
concurrency: concurrency,
saturated: null,
empty: null,
drain: null,
push: function (data, callback) {
_insert(q, data, false, callback);
},
unshift: function (data, callback) {
_insert(q, data, true, callback);
},
process: function () {
if (workers < q.concurrency && q.tasks.length) {
var task = q.tasks.shift();
if (q.empty && q.tasks.length === 0) {
q.empty();
}
workers += 1;
var next = function () {
workers -= 1;
if (task.callback) {
task.callback.apply(task, arguments);
}
if (q.drain && q.tasks.length + workers === 0) {
q.drain();
}
q.process();
};
var cb = only_once(next);
worker(task.data, cb);
}
},
length: function () {
return q.tasks.length;
},
running: function () {
return workers;
}
};
return q;
};
async.cargo = function (worker, payload) {
var working = false,
tasks = [];
var cargo = {
tasks: tasks,
payload: payload,
saturated: null,
empty: null,
drain: null,
push: function (data, callback) {
if(data.constructor !== Array) {
data = [data];
}
_each(data, function(task) {
tasks.push({
data: task,
callback: typeof callback === 'function' ? callback : null
});
if (cargo.saturated && tasks.length === payload) {
cargo.saturated();
}
});
async.setImmediate(cargo.process);
},
process: function process() {
if (working) return;
if (tasks.length === 0) {
if(cargo.drain) cargo.drain();
return;
}
var ts = typeof payload === 'number'
? tasks.splice(0, payload)
: tasks.splice(0);
var ds = _map(ts, function (task) {
return task.data;
});
if(cargo.empty) cargo.empty();
working = true;
worker(ds, function () {
working = false;
var args = arguments;
_each(ts, function (data) {
if (data.callback) {
data.callback.apply(null, args);
}
});
process();
});
},
length: function () {
return tasks.length;
},
running: function () {
return working;
}
};
return cargo;
};
var _console_fn = function (name) {
return function (fn) {
var args = Array.prototype.slice.call(arguments, 1);
fn.apply(null, args.concat([function (err) {
var args = Array.prototype.slice.call(arguments, 1);
if (typeof console !== 'undefined') {
if (err) {
if (console.error) {
console.error(err);
}
}
else if (console[name]) {
_each(args, function (x) {
console[name](x);
});
}
}
}]));
};
};
async.log = _console_fn('log');
async.dir = _console_fn('dir');
/*async.info = _console_fn('info');
async.warn = _console_fn('warn');
async.error = _console_fn('error');*/
async.memoize = function (fn, hasher) {
var memo = {};
var queues = {};
hasher = hasher || function (x) {
return x;
};
var memoized = function () {
var args = Array.prototype.slice.call(arguments);
var callback = args.pop();
var key = hasher.apply(null, args);
if (key in memo) {
callback.apply(null, memo[key]);
}
else if (key in queues) {
queues[key].push(callback);
}
else {
queues[key] = [callback];
fn.apply(null, args.concat([function () {
memo[key] = arguments;
var q = queues[key];
delete queues[key];
for (var i = 0, l = q.length; i < l; i++) {
q[i].apply(null, arguments);
}
}]));
}
};
memoized.memo = memo;
memoized.unmemoized = fn;
return memoized;
};
async.unmemoize = function (fn) {
return function () {
return (fn.unmemoized || fn).apply(null, arguments);
};
};
async.times = function (count, iterator, callback) {
var counter = [];
for (var i = 0; i < count; i++) {
counter.push(i);
}
return async.map(counter, iterator, callback);
};
async.timesSeries = function (count, iterator, callback) {
var counter = [];
for (var i = 0; i < count; i++) {
counter.push(i);
}
return async.mapSeries(counter, iterator, callback);
};
async.compose = function (/* functions... */) {
var fns = Array.prototype.reverse.call(arguments);
return function () {
var that = this;
var args = Array.prototype.slice.call(arguments);
var callback = args.pop();
async.reduce(fns, args, function (newargs, fn, cb) {
fn.apply(that, newargs.concat([function () {
var err = arguments[0];
var nextargs = Array.prototype.slice.call(arguments, 1);
cb(err, nextargs);
}]))
},
function (err, results) {
callback.apply(that, [err].concat(results));
});
};
};
var _applyEach = function (eachfn, fns /*args...*/) {
var go = function () {
var that = this;
var args = Array.prototype.slice.call(arguments);
var callback = args.pop();
return eachfn(fns, function (fn, cb) {
fn.apply(that, args.concat([cb]));
},
callback);
};
if (arguments.length > 2) {
var args = Array.prototype.slice.call(arguments, 2);
return go.apply(this, args);
}
else {
return go;
}
};
async.applyEach = doParallel(_applyEach);
async.applyEachSeries = doSeries(_applyEach);
async.forever = function (fn, callback) {
function next(err) {
if (err) {
if (callback) {
return callback(err);
}
throw err;
}
fn(next);
}
next();
};
// AMD / RequireJS
if (typeof define !== 'undefined' && define.amd) {
define([], function () {
return async;
});
}
// Node.js
else if (typeof module !== 'undefined' && module.exports) {
module.exports = async;
}
// included directly via <script> tag
else {
root.async = async;
}
}());

View File

@ -0,0 +1,60 @@
{
"name": "async",
"description": "Higher-order functions and common patterns for asynchronous code",
"main": "./lib/async",
"author": {
"name": "Caolan McMahon"
},
"version": "0.2.10",
"repository": {
"type": "git",
"url": "https://github.com/caolan/async.git"
},
"bugs": {
"url": "https://github.com/caolan/async/issues"
},
"licenses": [
{
"type": "MIT",
"url": "https://github.com/caolan/async/raw/master/LICENSE"
}
],
"devDependencies": {
"nodeunit": ">0.0.0",
"uglify-js": "1.2.x",
"nodelint": ">0.0.0"
},
"jam": {
"main": "lib/async.js",
"include": [
"lib/async.js",
"README.md",
"LICENSE"
]
},
"scripts": {
"test": "nodeunit test/test-async.js"
},
"_id": "async@0.2.10",
"dist": {
"shasum": "b6bbe0b0674b9d719708ca38de8c237cb526c3d1",
"tarball": "http://registry.npmjs.org/async/-/async-0.2.10.tgz"
},
"_from": "async@0.2.x",
"_npmVersion": "1.3.2",
"_npmUser": {
"name": "caolan",
"email": "caolan.mcmahon@gmail.com"
},
"maintainers": [
{
"name": "caolan",
"email": "caolan@caolanmcmahon.com"
}
],
"directories": {},
"_shasum": "b6bbe0b0674b9d719708ca38de8c237cb526c3d1",
"_resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz",
"readme": "ERROR: No README data found!",
"homepage": "https://github.com/caolan/async"
}

View File

@ -0,0 +1,4 @@
language: node_js
node_js:
- 0.4
- 0.6

View File

@ -0,0 +1,18 @@
This software is released under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,11 @@
var equal = require('../');
console.dir([
equal(
{ a : [ 2, 3 ], b : [ 4 ] },
{ a : [ 2, 3 ], b : [ 4 ] }
),
equal(
{ x : 5, y : [6] },
{ x : 5, y : 6 }
)
]);

View File

@ -0,0 +1,94 @@
var pSlice = Array.prototype.slice;
var objectKeys = require('./lib/keys.js');
var isArguments = require('./lib/is_arguments.js');
var deepEqual = module.exports = function (actual, expected, opts) {
if (!opts) opts = {};
// 7.1. All identical values are equivalent, as determined by ===.
if (actual === expected) {
return true;
} else if (actual instanceof Date && expected instanceof Date) {
return actual.getTime() === expected.getTime();
// 7.3. Other pairs that do not both pass typeof value == 'object',
// equivalence is determined by ==.
} else if (typeof actual != 'object' && typeof expected != 'object') {
return opts.strict ? actual === expected : actual == expected;
// 7.4. For all other Object pairs, including Array objects, equivalence is
// determined by having the same number of owned properties (as verified
// with Object.prototype.hasOwnProperty.call), the same set of keys
// (although not necessarily the same order), equivalent values for every
// corresponding key, and an identical 'prototype' property. Note: this
// accounts for both named and indexed properties on Arrays.
} else {
return objEquiv(actual, expected, opts);
}
}
function isUndefinedOrNull(value) {
return value === null || value === undefined;
}
function isBuffer (x) {
if (!x || typeof x !== 'object' || typeof x.length !== 'number') return false;
if (typeof x.copy !== 'function' || typeof x.slice !== 'function') {
return false;
}
if (x.length > 0 && typeof x[0] !== 'number') return false;
return true;
}
function objEquiv(a, b, opts) {
var i, key;
if (isUndefinedOrNull(a) || isUndefinedOrNull(b))
return false;
// an identical 'prototype' property.
if (a.prototype !== b.prototype) return false;
//~~~I've managed to break Object.keys through screwy arguments passing.
// Converting to array solves the problem.
if (isArguments(a)) {
if (!isArguments(b)) {
return false;
}
a = pSlice.call(a);
b = pSlice.call(b);
return deepEqual(a, b, opts);
}
if (isBuffer(a)) {
if (!isBuffer(b)) {
return false;
}
if (a.length !== b.length) return false;
for (i = 0; i < a.length; i++) {
if (a[i] !== b[i]) return false;
}
return true;
}
try {
var ka = objectKeys(a),
kb = objectKeys(b);
} catch (e) {//happens when one is a string literal and the other isn't
return false;
}
// having the same number of owned properties (keys incorporates
// hasOwnProperty)
if (ka.length != kb.length)
return false;
//the same set of keys (although not necessarily the same order),
ka.sort();
kb.sort();
//~~~cheap key test
for (i = ka.length - 1; i >= 0; i--) {
if (ka[i] != kb[i])
return false;
}
//equivalent values for every corresponding key, and
//~~~possibly expensive deep test
for (i = ka.length - 1; i >= 0; i--) {
key = ka[i];
if (!deepEqual(a[key], b[key], opts)) return false;
}
return typeof a === typeof b;
}

View File

@ -0,0 +1,20 @@
var supportsArgumentsClass = (function(){
return Object.prototype.toString.call(arguments)
})() == '[object Arguments]';
exports = module.exports = supportsArgumentsClass ? supported : unsupported;
exports.supported = supported;
function supported(object) {
return Object.prototype.toString.call(object) == '[object Arguments]';
};
exports.unsupported = unsupported;
function unsupported(object){
return object &&
typeof object == 'object' &&
typeof object.length == 'number' &&
Object.prototype.hasOwnProperty.call(object, 'callee') &&
!Object.prototype.propertyIsEnumerable.call(object, 'callee') ||
false;
};

View File

@ -0,0 +1,9 @@
exports = module.exports = typeof Object.keys === 'function'
? Object.keys : shim;
exports.shim = shim;
function shim (obj) {
var keys = [];
for (var key in obj) keys.push(key);
return keys;
}

View File

@ -0,0 +1,84 @@
{
"name": "deep-equal",
"version": "1.0.0",
"description": "node's assert.deepEqual algorithm",
"main": "index.js",
"directories": {
"lib": ".",
"example": "example",
"test": "test"
},
"scripts": {
"test": "tape test/*.js"
},
"devDependencies": {
"tape": "^3.5.0"
},
"repository": {
"type": "git",
"url": "http://github.com/substack/node-deep-equal.git"
},
"keywords": [
"equality",
"equal",
"compare"
],
"author": {
"name": "James Halliday",
"email": "mail@substack.net",
"url": "http://substack.net"
},
"license": "MIT",
"testling": {
"files": "test/*.js",
"browsers": {
"ie": [
6,
7,
8,
9
],
"ff": [
3.5,
10,
15
],
"chrome": [
10,
22
],
"safari": [
5.1
],
"opera": [
12
]
}
},
"gitHead": "39c740ebdafed9443912a4ef1493b18693934daf",
"bugs": {
"url": "https://github.com/substack/node-deep-equal/issues"
},
"homepage": "https://github.com/substack/node-deep-equal",
"_id": "deep-equal@1.0.0",
"_shasum": "d4564f07d2f0ab3e46110bec16592abd7dc2e326",
"_from": "deep-equal@*",
"_npmVersion": "2.3.0",
"_nodeVersion": "0.10.35",
"_npmUser": {
"name": "substack",
"email": "mail@substack.net"
},
"maintainers": [
{
"name": "substack",
"email": "mail@substack.net"
}
],
"dist": {
"shasum": "d4564f07d2f0ab3e46110bec16592abd7dc2e326",
"tarball": "http://registry.npmjs.org/deep-equal/-/deep-equal-1.0.0.tgz"
},
"_resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.0.tgz",
"readme": "ERROR: No README data found!"
}

View File

@ -0,0 +1,61 @@
# deep-equal
Node's `assert.deepEqual() algorithm` as a standalone module.
This module is around [5 times faster](https://gist.github.com/2790507)
than wrapping `assert.deepEqual()` in a `try/catch`.
[![browser support](https://ci.testling.com/substack/node-deep-equal.png)](https://ci.testling.com/substack/node-deep-equal)
[![build status](https://secure.travis-ci.org/substack/node-deep-equal.png)](https://travis-ci.org/substack/node-deep-equal)
# example
``` js
var equal = require('deep-equal');
console.dir([
equal(
{ a : [ 2, 3 ], b : [ 4 ] },
{ a : [ 2, 3 ], b : [ 4 ] }
),
equal(
{ x : 5, y : [6] },
{ x : 5, y : 6 }
)
]);
```
# methods
``` js
var deepEqual = require('deep-equal')
```
## deepEqual(a, b, opts)
Compare objects `a` and `b`, returning whether they are equal according to a
recursive equality algorithm.
If `opts.strict` is `true`, use strict equality (`===`) to compare leaf nodes.
The default is to use coercive equality (`==`) because that's how
`assert.deepEqual()` works by default.
# install
With [npm](http://npmjs.org) do:
```
npm install deep-equal
```
# test
With [npm](http://npmjs.org) do:
```
npm test
```
# license
MIT. Derived largely from node's assert module.

View File

@ -0,0 +1,89 @@
var test = require('tape');
var equal = require('../');
var isArguments = require('../lib/is_arguments.js');
var objectKeys = require('../lib/keys.js');
test('equal', function (t) {
t.ok(equal(
{ a : [ 2, 3 ], b : [ 4 ] },
{ a : [ 2, 3 ], b : [ 4 ] }
));
t.end();
});
test('not equal', function (t) {
t.notOk(equal(
{ x : 5, y : [6] },
{ x : 5, y : 6 }
));
t.end();
});
test('nested nulls', function (t) {
t.ok(equal([ null, null, null ], [ null, null, null ]));
t.end();
});
test('strict equal', function (t) {
t.notOk(equal(
[ { a: 3 }, { b: 4 } ],
[ { a: '3' }, { b: '4' } ],
{ strict: true }
));
t.end();
});
test('non-objects', function (t) {
t.ok(equal(3, 3));
t.ok(equal('beep', 'beep'));
t.ok(equal('3', 3));
t.notOk(equal('3', 3, { strict: true }));
t.notOk(equal('3', [3]));
t.end();
});
test('arguments class', function (t) {
t.ok(equal(
(function(){return arguments})(1,2,3),
(function(){return arguments})(1,2,3),
"compares arguments"
));
t.notOk(equal(
(function(){return arguments})(1,2,3),
[1,2,3],
"differenciates array and arguments"
));
t.end();
});
test('test the arguments shim', function (t) {
t.ok(isArguments.supported((function(){return arguments})()));
t.notOk(isArguments.supported([1,2,3]));
t.ok(isArguments.unsupported((function(){return arguments})()));
t.notOk(isArguments.unsupported([1,2,3]));
t.end();
});
test('test the keys shim', function (t) {
t.deepEqual(objectKeys.shim({ a: 1, b : 2 }), [ 'a', 'b' ]);
t.end();
});
test('dates', function (t) {
var d0 = new Date(1387585278000);
var d1 = new Date('Fri Dec 20 2013 16:21:18 GMT-0800 (PST)');
t.ok(equal(d0, d1));
t.end();
});
test('buffers', function (t) {
t.ok(equal(Buffer('xyz'), Buffer('xyz')));
t.end();
});
test('booleans and arrays', function (t) {
t.notOk(equal(true, []));
t.end();
})

View File

@ -0,0 +1,3 @@
node_modules
npm-debug.log
*.swp

View File

@ -0,0 +1,9 @@
language: node_js
node_js:
- 0.4
- 0.6
- 0.7
notifications:
irc: "irc.freenode.net#pksunkara"
email:
on_success: never

View File

@ -0,0 +1,18 @@
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,174 @@
# inflect
customizable inflections for nodejs
## Installation
```bash
npm install i
```
## Usage
Require the module before using
```js
var inflect = require('i')();
```
All the below api functions can be called directly on a string
```js
inflect.titleize('messages to store') // === 'Messages To Store'
'messages to store'.titleize // === 'Messages To Store'
```
only if `true` is passed while initiating
```js
var inflect = require('i')(true);
```
### Pluralize
```js
inflect.pluralize('person'); // === 'people'
inflect.pluralize('octopus'); // === 'octopi'
inflect.pluralize('Hat'); // === 'Hats'
```
### Singularize
```js
inflect.singularize('people'); // === 'person'
inflect.singularize('octopi'); // === 'octopus'
inflect.singularize('Hats'); // === 'Hat'
```
### Camelize
```js
inflect.camelize('message_properties'); // === 'MessageProperties'
inflect.camelize('message_properties', false); // === 'messageProperties'
```
### Underscore
```js
inflect.underscore('MessageProperties'); // === 'message_properties'
inflect.underscore('messageProperties'); // === 'message_properties'
```
### Humanize
```js
inflect.humanize('message_id'); // === 'Message'
```
### Dasherize
```js
inflect.dasherize('message_properties'); // === 'message-properties'
inflect.dasherize('Message Properties'); // === 'Message Properties'
```
### Titleize
```js
inflect.titleize('message_properties'); // === 'Message Properties'
inflect.titleize('message properties to keep'); // === 'Message Properties to Keep'
```
### Demodulize
```js
inflect.demodulize('Message.Bus.Properties'); // === 'Properties'
```
### Tableize
```js
inflect.tableize('MessageBusProperty'); // === 'message_bus_properties'
```
### Classify
```js
inflect.classify('message_bus_properties'); // === 'MessageBusProperty'
```
### Foreign key
```js
inflect.foreign_key('MessageBusProperty'); // === 'message_bus_property_id'
inflect.foreign_key('MessageBusProperty', false); // === 'message_bus_propertyid'
```
### Ordinalize
```js
inflect.ordinalize( '1' ); // === '1st'
```
## Custom rules for inflection
### Custom plural
We can use regexp in any of these custom rules
```js
inflect.inflections.plural('person', 'guys');
inflect.pluralize('person'); // === 'guys'
inflect.singularize('guys'); // === 'guy'
```
### Custom singular
```js
inflect.inflections.singular('guys', 'person')
inflect.singularize('guys'); // === 'person'
inflect.pluralize('person'); // === 'people'
```
### Custom irregular
```js
inflect.inflections.irregular('person', 'guys')
inflect.pluralize('person'); // === 'guys'
inflect.singularize('guys'); // === 'person'
```
### Custom human
```js
inflect.inflections.human(/^(.*)_cnt$/i, '$1_count');
inflect.inflections.humanize('jargon_cnt'); // === 'Jargon count'
```
### Custom uncountable
```js
inflect.inflections.uncountable('oil')
inflect.pluralize('oil'); // === 'oil'
inflect.singularize('oil'); // === 'oil'
```
## Contributors
Here is a list of [Contributors](http://github.com/pksunkara/inflect/contributors)
### TODO
- More obscure test cases
__I accept pull requests and guarantee a reply back within a day__
## License
MIT/X11
## Bug Reports
Report [here](http://github.com/pksunkara/inflect/issues). __Guaranteed reply within a day__.
## Contact
Pavan Kumar Sunkara (pavan.sss1991@gmail.com)
Follow me on [github](https://github.com/users/follow?target=pksunkara), [twitter](http://twitter.com/pksunkara)

View File

@ -0,0 +1,63 @@
// Default inflections
module.exports = function (inflect) {
inflect.plural(/$/, 's');
inflect.plural(/s$/i, 's');
inflect.plural(/(ax|test)is$/i, '$1es');
inflect.plural(/(octop|vir)us$/i, '$1i');
inflect.plural(/(octop|vir)i$/i, '$1i');
inflect.plural(/(alias|status)$/i, '$1es');
inflect.plural(/(bu)s$/i, '$1ses');
inflect.plural(/(buffal|tomat)o$/i, '$1oes');
inflect.plural(/([ti])um$/i, '$1a');
inflect.plural(/([ti])a$/i, '$1a');
inflect.plural(/sis$/i, 'ses');
inflect.plural(/(?:([^f])fe|([lr])f)$/i, '$1ves');
inflect.plural(/(hive)$/i, '$1s');
inflect.plural(/([^aeiouy]|qu)y$/i, '$1ies');
inflect.plural(/(x|ch|ss|sh)$/i, '$1es');
inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '$1ices');
inflect.plural(/([m|l])ouse$/i, '$1ice');
inflect.plural(/([m|l])ice$/i, '$1ice');
inflect.plural(/^(ox)$/i, '$1en');
inflect.plural(/^(oxen)$/i, '$1');
inflect.plural(/(quiz)$/i, '$1zes');
inflect.singular(/s$/i, '');
inflect.singular(/(n)ews$/i, '$1ews');
inflect.singular(/([ti])a$/i, '$1um');
inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, '$1sis');
inflect.singular(/(^analy)ses$/i, '$1sis');
inflect.singular(/([^f])ves$/i, '$1fe');
inflect.singular(/(hive)s$/i, '$1');
inflect.singular(/(tive)s$/i, '$1');
inflect.singular(/([lr])ves$/i, '$1f');
inflect.singular(/([^aeiouy]|qu)ies$/i, '$1y');
inflect.singular(/(s)eries$/i, '$1eries');
inflect.singular(/(m)ovies$/i, '$1ovie');
inflect.singular(/(x|ch|ss|sh)es$/i, '$1');
inflect.singular(/([m|l])ice$/i, '$1ouse');
inflect.singular(/(bus)es$/i, '$1');
inflect.singular(/(o)es$/i, '$1');
inflect.singular(/(shoe)s$/i, '$1');
inflect.singular(/(cris|ax|test)es$/i, '$1is');
inflect.singular(/(octop|vir)i$/i, '$1us');
inflect.singular(/(alias|status)es$/i, '$1');
inflect.singular(/^(ox)en/i, '$1');
inflect.singular(/(vert|ind)ices$/i, '$1ex');
inflect.singular(/(matr)ices$/i, '$1ix');
inflect.singular(/(quiz)zes$/i, '$1');
inflect.singular(/(database)s$/i, '$1');
inflect.irregular('child', 'children');
inflect.irregular('person', 'people');
inflect.irregular('man', 'men');
inflect.irregular('child', 'children');
inflect.irregular('sex', 'sexes');
inflect.irregular('move', 'moves');
inflect.irregular('cow', 'kine');
inflect.irregular('zombie', 'zombies');
inflect.uncountable(['equipment', 'information', 'rice', 'money', 'species', 'series', 'fish', 'sheep', 'jeans']);
}

View File

@ -0,0 +1,11 @@
// Requiring modules
module.exports = function (attach) {
var methods = require('./methods');
if (attach) {
require('./native')(methods);
}
return methods
};

View File

@ -0,0 +1,116 @@
// A singleton instance of this class is yielded by Inflector.inflections, which can then be used to specify additional
// inflection rules. Examples:
//
// BulletSupport.Inflector.inflect ($) ->
// $.plural /^(ox)$/i, '$1en'
// $.singular /^(ox)en/i, '$1'
//
// $.irregular 'octopus', 'octopi'
//
// $.uncountable "equipment"
//
// New rules are added at the top. So in the example above, the irregular rule for octopus will now be the first of the
// pluralization and singularization rules that is runs. This guarantees that your rules run before any of the rules that may
// already have been loaded.
var util = require('./util');
var Inflections = function () {
this.plurals = [];
this.singulars = [];
this.uncountables = [];
this.humans = [];
require('./defaults')(this);
return this;
};
// Specifies a new pluralization rule and its replacement. The rule can either be a string or a regular expression.
// The replacement should always be a string that may include references to the matched data from the rule.
Inflections.prototype.plural = function (rule, replacement) {
if (typeof rule == 'string') {
this.uncountables = util.array.del(this.uncountables, rule);
}
this.uncountables = util.array.del(this.uncountables, replacement);
this.plurals.unshift([rule, replacement]);
};
// Specifies a new singularization rule and its replacement. The rule can either be a string or a regular expression.
// The replacement should always be a string that may include references to the matched data from the rule.
Inflections.prototype.singular = function (rule, replacement) {
if (typeof rule == 'string') {
this.uncountables = util.array.del(this.uncountables, rule);
}
this.uncountables = util.array.del(this.uncountables, replacement);
this.singulars.unshift([rule, replacement]);
};
// Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used
// for strings, not regular expressions. You simply pass the irregular in singular and plural form.
//
// irregular 'octopus', 'octopi'
// irregular 'person', 'people'
Inflections.prototype.irregular = function (singular, plural) {
this.uncountables = util.array.del(this.uncountables, singular);
this.uncountables = util.array.del(this.uncountables, plural);
if (singular[0].toUpperCase() == plural[0].toUpperCase()) {
this.plural(new RegExp("(" + singular[0] + ")" + singular.slice(1) + "$", "i"), '$1' + plural.slice(1));
this.plural(new RegExp("(" + plural[0] + ")" + plural.slice(1) + "$", "i"), '$1' + plural.slice(1));
this.singular(new RegExp("(" + plural[0] + ")" + plural.slice(1) + "$", "i"), '$1' + singular.slice(1));
} else {
this.plural(new RegExp("" + (singular[0].toUpperCase()) + singular.slice(1) + "$"), plural[0].toUpperCase() + plural.slice(1));
this.plural(new RegExp("" + (singular[0].toLowerCase()) + singular.slice(1) + "$"), plural[0].toLowerCase() + plural.slice(1));
this.plural(new RegExp("" + (plural[0].toUpperCase()) + plural.slice(1) + "$"), plural[0].toUpperCase() + plural.slice(1));
this.plural(new RegExp("" + (plural[0].toLowerCase()) + plural.slice(1) + "$"), plural[0].toLowerCase() + plural.slice(1));
this.singular(new RegExp("" + (plural[0].toUpperCase()) + plural.slice(1) + "$"), singular[0].toUpperCase() + singular.slice(1));
this.singular(new RegExp("" + (plural[0].toLowerCase()) + plural.slice(1) + "$"), singular[0].toLowerCase() + singular.slice(1));
}
};
// Specifies a humanized form of a string by a regular expression rule or by a string mapping.
// When using a regular expression based replacement, the normal humanize formatting is called after the replacement.
// When a string is used, the human form should be specified as desired (example: 'The name', not 'the_name')
//
// human /(.*)_cnt$/i, '$1_count'
// human "legacy_col_person_name", "Name"
Inflections.prototype.human = function (rule, replacement) {
this.humans.unshift([rule, replacement]);
}
// Add uncountable words that shouldn't be attempted inflected.
//
// uncountable "money"
// uncountable ["money", "information"]
Inflections.prototype.uncountable = function (words) {
this.uncountables = this.uncountables.concat(words);
}
// Clears the loaded inflections within a given scope (default is _'all'_).
// Give the scope as a symbol of the inflection type, the options are: _'plurals'_,
// _'singulars'_, _'uncountables'_, _'humans'_.
//
// clear 'all'
// clear 'plurals'
Inflections.prototype.clear = function (scope) {
if (scope == null) scope = 'all';
switch (scope) {
case 'all':
this.plurals = [];
this.singulars = [];
this.uncountables = [];
this.humans = [];
default:
this[scope] = [];
}
}
// Clears the loaded inflections and initializes them to [default](../inflections.html)
Inflections.prototype.default = function () {
this.plurals = [];
this.singulars = [];
this.uncountables = [];
this.humans = [];
require('./defaults')(this);
return this;
};
module.exports = new Inflections();

View File

@ -0,0 +1,233 @@
// The Inflector transforms words from singular to plural, class names to table names, modularized class names to ones without,
// and class names to foreign keys. The default inflections for pluralization, singularization, and uncountable words are kept
// in inflections.coffee
//
// If you discover an incorrect inflection and require it for your application, you'll need
// to correct it yourself (explained below).
var util = require('./util');
var inflect = module.exports;
// Import [inflections](inflections.html) instance
inflect.inflections = require('./inflections')
// Gives easy access to add inflections to this class
inflect.inflect = function (inflections_function) {
inflections_function(inflect.inflections);
};
// By default, _camelize_ converts strings to UpperCamelCase. If the argument to _camelize_
// is set to _false_ then _camelize_ produces lowerCamelCase.
//
// _camelize_ will also convert '/' to '.' which is useful for converting paths to namespaces.
//
// "bullet_record".camelize() // => "BulletRecord"
// "bullet_record".camelize(false) // => "bulletRecord"
// "bullet_record/errors".camelize() // => "BulletRecord.Errors"
// "bullet_record/errors".camelize(false) // => "bulletRecord.Errors"
//
// As a rule of thumb you can think of _camelize_ as the inverse of _underscore_,
// though there are cases where that does not hold:
//
// "SSLError".underscore.camelize // => "SslError"
inflect.camelize = function(lower_case_and_underscored_word, first_letter_in_uppercase) {
var result;
if (first_letter_in_uppercase == null) first_letter_in_uppercase = true;
result = util.string.gsub(lower_case_and_underscored_word, /\/(.?)/, function($) {
return "." + (util.string.upcase($[1]));
});
result = util.string.gsub(result, /(?:_)(.)/, function($) {
return util.string.upcase($[1]);
});
if (first_letter_in_uppercase) {
return util.string.upcase(result);
} else {
return util.string.downcase(result);
}
};
// Makes an underscored, lowercase form from the expression in the string.
//
// Changes '.' to '/' to convert namespaces to paths.
//
// "BulletRecord".underscore() // => "bullet_record"
// "BulletRecord.Errors".underscore() // => "bullet_record/errors"
//
// As a rule of thumb you can think of +underscore+ as the inverse of +camelize+,
// though there are cases where that does not hold:
//
// "SSLError".underscore().camelize() // => "SslError"
inflect.underscore = function (camel_cased_word) {
var self;
self = util.string.gsub(camel_cased_word, /\./, '/');
self = util.string.gsub(self, /([A-Z]+)([A-Z][a-z])/, "$1_$2");
self = util.string.gsub(self, /([a-z\d])([A-Z])/, "$1_$2");
self = util.string.gsub(self, /-/, '_');
return self.toLowerCase();
};
// Replaces underscores with dashes in the string.
//
// "puni_puni".dasherize() // => "puni-puni"
inflect.dasherize = function (underscored_word) {
return util.string.gsub(underscored_word, /_/, '-');
};
// Removes the module part from the expression in the string.
//
// "BulletRecord.String.Inflections".demodulize() // => "Inflections"
// "Inflections".demodulize() // => "Inflections"
inflect.demodulize = function (class_name_in_module) {
return util.string.gsub(class_name_in_module, /^.*\./, '');
};
// Creates a foreign key name from a class name.
// _separate_class_name_and_id_with_underscore_ sets whether
// the method should put '_' between the name and 'id'.
//
// "Message".foreign_key() // => "message_id"
// "Message".foreign_key(false) // => "messageid"
// "Admin::Post".foreign_key() // => "post_id"
inflect.foreign_key = function (class_name, separate_class_name_and_id_with_underscore) {
if (separate_class_name_and_id_with_underscore == null) {
separate_class_name_and_id_with_underscore = true;
}
return inflect.underscore(inflect.demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id");
};
// Turns a number into an ordinal string used to denote the position in an
// ordered sequence such as 1st, 2nd, 3rd, 4th.
//
// ordinalize(1) // => "1st"
// ordinalize(2) // => "2nd"
// ordinalize(1002) // => "1002nd"
// ordinalize(1003) // => "1003rd"
// ordinalize(-11) // => "-11th"
// ordinalize(-1021) // => "-1021st"
inflect.ordinalize = function (number) {
var _ref;
number = parseInt(number);
if ((_ref = Math.abs(number) % 100) === 11 || _ref === 12 || _ref === 13) {
return "" + number + "th";
} else {
switch (Math.abs(number) % 10) {
case 1:
return "" + number + "st";
case 2:
return "" + number + "nd";
case 3:
return "" + number + "rd";
default:
return "" + number + "th";
}
}
};
// Checks a given word for uncountability
//
// "money".uncountability() // => true
// "my money".uncountability() // => true
inflect.uncountability = function (word) {
return inflect.inflections.uncountables.some(function(ele, ind, arr) {
return word.match(new RegExp("(\\b|_)" + ele + "$", 'i')) != null;
});
};
// Returns the plural form of the word in the string.
//
// "post".pluralize() // => "posts"
// "octopus".pluralize() // => "octopi"
// "sheep".pluralize() // => "sheep"
// "words".pluralize() // => "words"
// "CamelOctopus".pluralize() // => "CamelOctopi"
inflect.pluralize = function (word) {
var plural, result;
result = word;
if (word === '' || inflect.uncountability(word)) {
return result;
} else {
for (var i = 0; i < inflect.inflections.plurals.length; i++) {
plural = inflect.inflections.plurals[i];
result = util.string.gsub(result, plural[0], plural[1]);
if (word.match(plural[0]) != null) break;
}
return result;
}
};
// The reverse of _pluralize_, returns the singular form of a word in a string.
//
// "posts".singularize() // => "post"
// "octopi".singularize() // => "octopus"
// "sheep".singularize() // => "sheep"
// "word".singularize() // => "word"
// "CamelOctopi".singularize() // => "CamelOctopus"
inflect.singularize = function (word) {
var result, singular;
result = word;
if (word === '' || inflect.uncountability(word)) {
return result;
} else {
for (var i = 0; i < inflect.inflections.singulars.length; i++) {
singular = inflect.inflections.singulars[i];
result = util.string.gsub(result, singular[0], singular[1]);
if (word.match(singular[0])) break;
}
return result;
}
};
// Capitalizes the first word and turns underscores into spaces and strips a
// trailing "_id", if any. Like _titleize_, this is meant for creating pretty output.
//
// "employee_salary".humanize() // => "Employee salary"
// "author_id".humanize() // => "Author"
inflect.humanize = function (lower_case_and_underscored_word) {
var human, result;
result = lower_case_and_underscored_word;
for (var i = 0; i < inflect.inflections.humans.length; i++) {
human = inflect.inflections.humans[i];
result = util.string.gsub(result, human[0], human[1]);
}
result = util.string.gsub(result, /_id$/, "");
result = util.string.gsub(result, /_/, " ");
return util.string.capitalize(result, true);
};
// Capitalizes all the words and replaces some characters in the string to create
// a nicer looking title. _titleize_ is meant for creating pretty output. It is not
// used in the Bullet internals.
//
//
// "man from the boondocks".titleize() // => "Man From The Boondocks"
// "x-men: the last stand".titleize() // => "X Men: The Last Stand"
inflect.titleize = function (word) {
var self;
self = inflect.humanize(inflect.underscore(word));
self = util.string.gsub(self, /[^a-zA-Z:']/, ' ');
return util.string.capitalize(self);
};
// Create the name of a table like Bullet does for models to table names. This method
// uses the _pluralize_ method on the last word in the string.
//
// "RawScaledScorer".tableize() // => "raw_scaled_scorers"
// "egg_and_ham".tableize() // => "egg_and_hams"
// "fancyCategory".tableize() // => "fancy_categories"
inflect.tableize = function (class_name) {
return inflect.pluralize(inflect.underscore(class_name));
};
// Create a class name from a plural table name like Bullet does for table names to models.
// Note that this returns a string and not a Class.
//
// "egg_and_hams".classify() // => "EggAndHam"
// "posts".classify() // => "Post"
//
// Singular names are not handled correctly:
//
// "business".classify() // => "Busines"
inflect.classify = function (table_name) {
return inflect.camelize(inflect.singularize(util.string.gsub(table_name, /.*\./, '')));
}

View File

@ -0,0 +1,26 @@
module.exports = function (obj) {
var addProperty = function (method, func) {
String.prototype.__defineGetter__(method, func);
}
var stringPrototypeBlacklist = [
'__defineGetter__', '__defineSetter__', '__lookupGetter__', '__lookupSetter__', 'charAt', 'constructor',
'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'valueOf', 'charCodeAt',
'indexOf', 'lastIndexof', 'length', 'localeCompare', 'match', 'replace', 'search', 'slice', 'split', 'substring',
'toLocaleLowerCase', 'toLocaleUpperCase', 'toLowerCase', 'toUpperCase', 'trim', 'trimLeft', 'trimRight', 'gsub'
];
Object.keys(obj).forEach(function (key) {
if (key != 'inflect' && key != 'inflections') {
if (stringPrototypeBlacklist.indexOf(key) !== -1) {
console.log('warn: You should not override String.prototype.' + key);
} else {
addProperty(key, function () {
return obj[key](this);
});
}
}
});
}

View File

@ -0,0 +1,136 @@
// Some utility functions in js
var u = module.exports = {
array: {
// Returns a copy of the array with the value removed once
//
// [1, 2, 3, 1].del 1 #=> [2, 3, 1]
// [1, 2, 3].del 4 #=> [1, 2, 3]
del: function (arr, val) {
var index = arr.indexOf(val);
if (index != -1) {
if (index == 0) {
return arr.slice(1)
} else {
return arr.slice(0, index).concat(arr.slice(index+1));
}
} else {
return arr;
}
},
// Returns the first element of the array
//
// [1, 2, 3].first() #=> 1
first: function(arr) {
return arr[0];
},
// Returns the last element of the array
//
// [1, 2, 3].last() #=> 3
last: function(arr) {
return arr[arr.length-1];
}
},
string: {
// Returns a copy of str with all occurrences of pattern replaced with either replacement or the return value of a function.
// The pattern will typically be a Regexp; if it is a String then no regular expression metacharacters will be interpreted
// (that is /\d/ will match a digit, but \d will match a backslash followed by a d).
//
// In the function form, the current match object is passed in as a parameter to the function, and variables such as
// $[1], $[2], $[3] (where $ is the match object) will be set appropriately. The value returned by the function will be
// substituted for the match on each call.
//
// The result inherits any tainting in the original string or any supplied replacement string.
//
// "hello".gsub /[aeiou]/, '*' #=> "h*ll*"
// "hello".gsub /[aeiou]/, '<$1>' #=> "h<e>ll<o>"
// "hello".gsub /[aeiou]/, ($) {
// "<#{$[1]}>" #=> "h<e>ll<o>"
//
gsub: function (str, pattern, replacement) {
var i, match, matchCmpr, matchCmprPrev, replacementStr, result, self;
if (!((pattern != null) && (replacement != null))) return u.string.value(str);
result = '';
self = str;
while (self.length > 0) {
if ((match = self.match(pattern))) {
result += self.slice(0, match.index);
if (typeof replacement === 'function') {
match[1] = match[1] || match[0];
result += replacement(match);
} else if (replacement.match(/\$[1-9]/)) {
matchCmprPrev = match;
matchCmpr = u.array.del(match, void 0);
while (matchCmpr !== matchCmprPrev) {
matchCmprPrev = matchCmpr;
matchCmpr = u.array.del(matchCmpr, void 0);
}
match[1] = match[1] || match[0];
replacementStr = replacement;
for (i = 1; i <= 9; i++) {
if (matchCmpr[i]) {
replacementStr = u.string.gsub(replacementStr, new RegExp("\\\$" + i), matchCmpr[i]);
}
}
result += replacementStr;
} else {
result += replacement;
}
self = self.slice(match.index + match[0].length);
} else {
result += self;
self = '';
}
}
return result;
},
// Returns a copy of the String with the first letter being upper case
//
// "hello".upcase #=> "Hello"
upcase: function(str) {
var self = u.string.gsub(str, /_([a-z])/, function ($) {
return "_" + $[1].toUpperCase();
});
self = u.string.gsub(self, /\/([a-z])/, function ($) {
return "/" + $[1].toUpperCase();
});
return self[0].toUpperCase() + self.substr(1);
},
// Returns a copy of capitalized string
//
// "employee salary" #=> "Employee Salary"
capitalize: function (str, spaces) {
var self = str.toLowerCase();
if(!spaces) {
self = u.string.gsub(self, /\s([a-z])/, function ($) {
return " " + $[1].toUpperCase();
});
}
return self[0].toUpperCase() + self.substr(1);
},
// Returns a copy of the String with the first letter being lower case
//
// "HELLO".downcase #=> "hELLO"
downcase: function(str) {
var self = u.string.gsub(str, /_([A-Z])/, function ($) {
return "_" + $[1].toLowerCase();
});
self = u.string.gsub(self, /\/([A-Z])/, function ($) {
return "/" + $[1].toLowerCase();
});
return self[0].toLowerCase() + self.substr(1);
},
// Returns a string value for the String object
//
// "hello".value() #=> "hello"
value: function (str) {
return str.substr(0);
}
}
}

View File

@ -0,0 +1,79 @@
{
"name": "i",
"version": "0.3.3",
"author": {
"name": "Pavan Kumar Sunkara",
"email": "pavan.sss1991@gmail.com",
"url": "pksunkara.github.com"
},
"description": "custom inflections for nodejs",
"main": "./lib/inflect",
"repository": {
"type": "git",
"url": "git://github.com/pksunkara/inflect.git"
},
"keywords": [
"singular",
"plural",
"camelize",
"underscore",
"dasherize",
"demodulize",
"ordinalize",
"uncountable",
"pluralize",
"singularize",
"titleize",
"tableize",
"classify",
"foreign_key"
],
"homepage": "http://pksunkara.github.com/inflect",
"scripts": {
"test": "vows --spec $(find test -name '*-test.js')"
},
"contributors": [
{
"name": "Pavan Kumar Sunkara",
"email": "pavan.sss1991@gmail.com"
}
],
"dependencies": {},
"devDependencies": {
"vows": "~0.6.1"
},
"engines": {
"node": ">=0.4"
},
"bugs": {
"url": "https://github.com/pksunkara/inflect/issues"
},
"licenses": [
{
"type": "MIT",
"url": "https://github.com/pksunkara/inflect/raw/master/LICENSE"
}
],
"gitHead": "f6495e42873fe5b3ca80b060bc274662c3ed610b",
"_id": "i@0.3.3",
"_shasum": "0ff9a5eb743504aa8ac26a84f84c641287ff24cd",
"_from": "i@0.3.x",
"_npmVersion": "2.0.0",
"_npmUser": {
"name": "pksunkara",
"email": "pavan.sss1991@gmail.com"
},
"maintainers": [
{
"name": "pksunkara",
"email": "pavan.sss1991@gmail.com"
}
],
"dist": {
"shasum": "0ff9a5eb743504aa8ac26a84f84c641287ff24cd",
"tarball": "http://registry.npmjs.org/i/-/i-0.3.3.tgz"
},
"directories": {},
"_resolved": "https://registry.npmjs.org/i/-/i-0.3.3.tgz",
"readme": "ERROR: No README data found!"
}

Some files were not shown because too many files have changed in this diff Show More